from Init import *

import tkinter as tk
from tkinter import messagebox
import time


# 网络初始化相关 =============================
NETTIME_STAMP = None  # 网络时间戳
NETTIME_ARRAY = None  # 网络时间数组
NETTIME = None  # 网络时间字符串 '2021-07-11 15:14:07'
NETTIME_D = None  # 网络日期（'2021-07-11'）
NETTIME_T = None  # 网络时间（'15:14:07'）
# --【TIMESTART】--
# 为了不频繁访问获取时间的接口，软件打开时获取一次网络时间，同时开启计时
# 当前网络时间戳 = 当前计时时间戳 - 开始计时时间戳 + 网络时间戳
TIMESTART = None


def handle_time(time_stamp):
    """时间处理"""

    global NETTIME_ARRAY, NETTIME, NETTIME_T, NETTIME_D

    # tm_year	2008
    # tm_mon	1 到 12
    # tm_mday	1 到 31
    # tm_hour	0 到 23
    # tm_min	0 到 59
    # tm_sec	0 到 61 (60或61 是闰秒)
    # tm_wday	0到6 (0是周一)
    # tm_yday	一年中的第几天，1 到 366
    # tm_isdst	是否为夏令时，值有：1(夏令时)、0(不是夏令时)、-1(未知)，默认 -1
    # 时间数组 (tm_year=2021, tm_mon=7, tm_mday=11, tm_hour=15, tm_min=17, tm_sec=50, tm_wday=6, tm_yday=192, tm_isdst=0)
    NETTIME_ARRAY = time.localtime(time_stamp)
    # 将时间数组转换为字符串格式 '2021-07-11 15:14:07'
    NETTIME = time.strftime('%Y-%m-%d %H:%M:%S', NETTIME_ARRAY)  # 转换为时间字符串
    NETTIME_D, NETTIME_T = NETTIME.split(' ')  # 分割为日期（'2021-07-11'） 和 时间（'15:14:07'）


def calibration_time(often=False):
    """
    校准网络时间，如果校准时间成功全局变量 NETTIME_STAMP 为 int 类型
    其他说明，校准失败：
        【'Time out'】 - 请求超时
        【'Network error'】 - 网络连接错误

    :param often: 是否开启无延时访问
    """
    global NETTIME_STAMP, TIMESTART

    # 访问东方财富官网
    NETTIME_STAMP = Inet.connect_dfcf(often=often)  # 返回如果成功，返回int类型秒级时间戳
    if isinstance(NETTIME_STAMP, int):
        TIMESTART = time.time()
        handle_time(NETTIME_STAMP)


def calc_time():
    """
    计算网络时间

    :return:
        【Ture】 - 计算时间成功
        【False】 - 计算时间失败
    """
    if isinstance(NETTIME_STAMP, int):
        differ = time.time() - TIMESTART
        handle_time(TIMESTART+differ)
        return True
    else:
        return False


def isopen():
    """
    判断当前交易状态
        交易前：0:00 - 9:30  [0 - 570]
        交易中场休息：11:31 - 13:00  [691 - 780]
        交易中：9:31 - 11:30  or 13:01 - 15:00  [571 - 690] or [781 - 900]
        交易结束：15:01 - 24:00  [901 - 1440]
    获取当前时间戳，当前时间戳 = 获取的网络时间戳 + （开始计时cpu时间戳 - 当前cpu时间戳）
    :return:
        【int】 -
            >0 处在交易前或者中场休息。数值为需要还有多久开始交易
            <0 交易已结束
            0 交易中
    """

    # 将时间转换为分钟
    nowtime_min = NETTIME_ARRAY.tm_hour * 60 + NETTIME_ARRAY.tm_min
    # print(f"当前时间：{NETTIME_ARRAY.tm_hour}:{NETTIME_ARRAY.tm_min}. 分钟：{nowtime_min}")

    # test
    # nowtime_min = 570

    sleep_s = -1
    # 判断交易时间
    if nowtime_min < 571:
        sleep_s = 571 - nowtime_min
        # print(f'交易前，交易倒计时(min): {sleep_s}')

    elif nowtime_min > 900:
        sleep_s = -1
        # print(f'交易结束，交易倒计时(min): {sleep_s}')

    elif 690 < nowtime_min < 781:
        sleep_s = 781 - nowtime_min
        # print(f'交易中场休息，交易倒计时(min): {sleep_s}')

    elif 571 <= nowtime_min <= 690 or 781 <= nowtime_min <= 900:
        sleep_s = 0
        # print(f'交易中，交易倒计时(min): {sleep_s}')

    return sleep_s * 60


def only_number(content):
    """
    限制Entry只能输入数字

    :param content:
    :return:
    """
    # 如果不加上==""的话，就会发现删不完。总会剩下一个数字 isdigit函数：isdigit函数方法检测字符串是否只由数字组成。
    if content.isdigit() or content == "":
        return True
    else:
        return False


def handle_paging(ready_list, page_num):
    """
    处理分页
    :param ready_list: 需要分页的数据列 [[],[],[]]
    :param page_num: 每页的数量
    :return:
        处理后的分页列表
        [[1,2],[1,2],]
    """
    # print(f'需要处理的列表：{ready_list}')
    # 判断分页数量
    page_list = []  # 处理完分页的数据
    total_num = len(ready_list)  # 数据总条目
    if total_num > page_num:
        # 需要分页
        aline_num = 0  # 如果 aline_num == page_num. 一页
        apage = []  # 一页的数据
        for cell in ready_list:
            apage.append(cell)
            aline_num += 1  # 一页数据标志位
            if aline_num == page_num:
                # 一页数据处理完成
                page_list.append(apage)
                apage = []  # 清空一页，准备下一页
                aline_num = 0
        # 判断是否整除，不整除，说明有溢出的数据
        if total_num % page_num != 0:
            page_list.append(tuple(apage))
    else:
        # 如果总数据条码 <= 分页数据条码。直接一页
        page_list.append(tuple(ready_list))

    return tuple(page_list)


def draw_tb_line(cvs, cvs_atts, tt=(1, 1), line_wight=SIZE_TBLINE_WIDTH, line_color=COLOR_TBLINE):
    """
    绘制表格线条

    :param cvs: 画布对象
    :param cvs_atts: 表头画布属性
    :param tt: 顶部线还是底部线
    :param line_wight: 线条宽度
    :param line_color: 线条颜色
    :return:
    """

    if tt[0]:
        # 绘制上方线条
        top_line = (0, 0, cvs_atts['width'], 0)
        cvs.create_line(top_line, width=line_wight, fill=line_color)
    if tt[1]:
        # 绘制下方线条
        bottom_line = (0, cvs_atts['height'] - 1, cvs_atts['width'], cvs_atts['height'] - 1)
        cvs.create_line(bottom_line, width=line_wight, fill=line_color)


def gets_jjheld(jjinfo_list: list, year):
    """
    遍历获取基金持仓

    :param jjinfo_list: 基金信息列表 [[基金代码, 基金名称],......]
    :param year: 获取哪一年的
    :return:
        【'Time out'】 - 请求超时
        【'Network error'】 - 网络连接错误
        【list】 - [
            {
                'jjInfo': ['519674', '银河创新成长混合'],
                'jjHeld': [
                    [
                        '//quote.eastmoney.com/sz300661.html', '1', '300661', '圣邦股份', '', '', '变动详情股吧行情档案',
                        '10.45%', '1,164.74', '294,365.42'], ['//quote.eastmoney.com/sh603986.html', '2', '603986',
                        '兆易创新', '', '', '变动详情股吧行情档案', '10.22%', '1,532.52', '287,960.56'
                    ], ......
                ]
            },
            ......
        ]
    """

    jjheld_list = []  # 存储基金持仓数据
    # [
    #   {
    #       'jjInfo': ['519674', '银河创新成长混合'],
    #       'jjHeld': [
    #           [
    #               '//quote.eastmoney.com/sz300661.html', '1', '300661', '圣邦股份', '', '', '变动详情股吧行情档案',
    #               '10.45%', '1,164.74', '294,365.42'], ['//quote.eastmoney.com/sh603986.html', '2', '603986',
    #               '兆易创新', '', '', '变动详情股吧行情档案', '10.22%', '1,532.52', '287,960.56'
    #           ], ......
    #       ]
    #   }, ......
    # ]
    # 遍历基金，获取基金持仓
    for jjinfo in jjinfo_list:
        print(f'正在获取：{jjinfo} 持仓信息！')
        # 将获取到的基金信息格式统一
        # 'jjInfo': [基金代码, 基金名称]
        noe_jj = {
            'jjInfo': jjinfo,
            'jjHeld': None
        }
        # 按照基金代码查询历史持仓
        response = Inet.get_jjheld(jjinfo[0], year)
        if not isinstance(response, dict):
            if response is None:
                # 该基金暂无最新持仓
                print(f'{jjinfo[1]} {year} 暂无持仓！')
                response = {
                    '持仓': {
                        '数据': []
                    }
                }
            else:
                # 其他情况, 即（'Time out'，'Network error'）直接结束
                return response
        # 存储一只基金的持仓数据
        noe_jj['jjHeld'] = response['持仓']['数据']
        # 汇总基金信息
        jjheld_list.append(noe_jj)

    return jjheld_list


def td_byjjrank(ft: str, sc: str, pn: int):
    """
    按照基金排名，获取基金持仓，并生成推荐指数

    :param ft: 类型（全部:all 股票：gp 混合：hh 指数：zs）
    :param sc: 排行时间段（近一周：zzf 近1月：1yzf 近3月：3yzf 近6月：6yzf 近1年：1nzf 近3年：3nzf）
    :param pn: 显示排行数前多少名
    :return:
        【dict】 - {
                'TabelName': '按基金排行（全部,近1月,前50）',
                'data': -
                    【'Time out'】 - 请求超时
                    【'Network error'】 - 网络连接错误
                    【'Response error'】 - 按基金名称查询接口，响应内容可能变更
                    【dict】 - 数据分析成功
            }
    """
    # 获取前端选择参数
    option = {
        'ft': {
            'all': '全部',
            'gp': '股票',
            'hh': '混合',
            'zs': '指数',
        },
        'sc': {
            'zzf': '近1周',
            '1yzf': '近1月',
            '3yzf': '近3月',
            '6yzf': '近6月',
            'nzf': '近1年',
            '3nzf': '近3年',
        },
    }
    tdResult = {
        'TabelName': '按基金排行（' + option['ft'][ft] + ',' + option['sc'][sc] + ',前' + str(pn) + '）',
        'data': None
    }

    # 获取基金排名
    jjrank = Inet.get_jjrank(ft, sc, pn)
    if not isinstance(jjrank, list):
        # 访问出错
        tdResult['data'] = jjrank
        return tdResult
    print(f'按基金排行，获取到的基金排名: {jjrank}')
    # [
    #   [
    #       0_"基金代码", 1_"基金简称", 2_"首字母", 3_"最新日期", 4_"最新净值", 5_"累计净值", 6_"日增长率", 7_"近1周收益率",
    #       8_"近1月收益率", 9_"近3月收益率", 10_"近6月收益率", 11_"近1年收益率", 12_"近2年收益率", 13_"近3年收益率",
    #       14_"今年来收益率", 15_"成立以来收益率", 16_"成立日期", 17_"Unknown1", 18_"自定义收益率", 19_"Unknown2",
    #       20_"Unknown3", 21_"Unknown4", 22_"手续费", 23_"Unknown5"
    #   ], ......
    # ]
    # 校准时间
    calibration_time()
    if not isinstance(NETTIME_STAMP, int):
        # 校准网络时间失败
        tdResult['data'] = NETTIME_STAMP
        return tdResult
    year = NETTIME_ARRAY.tm_year
    # print(f'当前年份：{year}')

    # 处理获取到的基金列表
    jjinfo_list = []
    for noe in jjrank:
        jjinfo_list.append([noe[0], noe[1]])
    # 获取基金持仓
    jjheld_list = gets_jjheld(jjinfo_list, year)
    if not isinstance(jjheld_list, list):
        tdResult['data'] = jjheld_list
        return tdResult
    # 基金持仓获取成功
    print(f'按照基金排名，汇总完的基金持仓信息: {jjheld_list}')
    # 统计基金持仓，并生成个股推荐指数
    rslt = Anls.stock_rating(jjheld_list, NETTIME_D)
    if not isinstance(rslt, dict):
        # 返回不是字典，访问错误
        tdResult['data'] = rslt
        return tdResult
    # print(f'按股票排行分析的最终结果：{rslt}')

    # 返回线程结果
    tdResult['data'] = rslt
    return tdResult


def td_byjjname(key: str):
    """
    按照基金名称，获取基金持仓，并生成推荐指数

    :param key:
    :return:
        【dict】 - {
                'TabelName': '按基金排行（全部,近1月,前50）',
                'data': -
                    【'Time out'】 - 请求超时
                    【'Network error'】 - 网络连接错误
                    【'Response error'】 - 按基金名称查询接口，响应内容可能变更
                    【dict】 - 数据分析成功
            }
    """
    # print(f'按照 {key} 搜索基金...')
    tdResult = {
        'TabelName': '按基金名称（' + str(key) + '）',
        'data': None
    }

    # 按键名称搜索基金
    byjjname_list = Inet.get_jjbyname(key)
    if not isinstance(byjjname_list, list):
        # 访问出错
        tdResult['data'] = byjjname_list
        return tdResult
    print(f'按基金名称获取到的基金列表：{byjjname_list}')
    # [
    #   {'_id': '159766', 'CODE': '159766', 'NAME': '富国中证旅游主题ETF', 'STOCKMARKET': ''},
    #   ......
    # ]
    # 校准时间
    calibration_time()
    if not isinstance(NETTIME_STAMP, int):
        # 校准网络时间失败
        tdResult['data'] = NETTIME_STAMP
        return tdResult
    year = NETTIME_ARRAY.tm_year
    # print(f'当前年份：{year}')

    # 处理获取到的基金列表
    jjinfo_list = []
    for noe in byjjname_list:
        jjinfo_list.append([noe['CODE'], noe['NAME']])
    # 获取基金持仓
    jjheld_list = gets_jjheld(jjinfo_list, year)
    if not isinstance(jjheld_list, list):
        tdResult['data'] = jjheld_list
        return tdResult
    # 基金持仓获取成功
    print(f'按照基金名称，汇总完的基金持仓信息: {jjheld_list}')
    # 统计基金持仓，并生成个股推荐指数
    rslt = Anls.stock_rating(jjheld_list, NETTIME_D)
    if not isinstance(rslt, dict):
        # 返回不是字典，访问错误
        return rslt

    tdResult['data'] = rslt
    # 返回线程结果
    return tdResult


def init_ready():
    """
    判断所有初始化是否准备完毕

    """
    # 判断表格初始化是否完成，失败直接退出
    if TABELINIT != 'success':
        # 表格初始化失败
        messagebox.showerror(title='代码错误', message=TABELINIT)
        exit()  # 退出程序

    # 检测网络状况
    calibration_time(often=True)  # 校准时间, 开启无延时访问
    # 循环检测网络连接情况，直到用户取消或连接成功！
    while not isinstance(NETTIME_STAMP, int):
        if NETTIME_STAMP == 'Time out':
            # 超时，询问是否重新连接
            asktry = messagebox.askretrycancel(
                title='连接超时',
                message='连接超时，是否重新连接？如果取消，部分功能可能无法使用！'
            )
            if asktry:
                # 重新检查
                calibration_time()
            else:
                # 用户取消
                break
        elif NETTIME_STAMP == 'Network error':
            # 如果是网络错误，直接不再检查！
            messagebox.showerror(
                title='网络连接错误',
                message='网络连接错误，部分功能可能无法使用！！'
            )
            break

    # 判断需要图片压缩包是否存在
    if not SKINZIP:
        print(f'Skin.zip准备失败！')
        messagebox.showerror(
            title='必要文件缺失',
            message='必要文件缺失，请尝试重新安装！！'
        )
        exit()  # 退出程序


class WINDOW:
    def __init__(self):
        # 创建主窗口
        self.ROOT = tk.Tk()
        # 获取屏幕信息
        SYS_W = self.ROOT.winfo_screenwidth()  # 屏幕宽度
        SYS_H = self.ROOT.winfo_screenheight()  # 屏幕高度
        # 计算居中的位置
        WIN_X = (SYS_W - SIZE_WIN_W) / 2
        WIN_Y = (SYS_H - SIZE_WIN_H) / 2
        self.ROOT.geometry("%dx%d+%d+%d" % (SIZE_WIN_W, SIZE_WIN_H, WIN_X, WIN_Y))  # 主窗口居中显示
        self.ROOT.resizable(width=False, height=False)  # 禁止改变窗体大小
        self.ROOT.title("ChoiceStock")  # 窗口标题
        self.ROOT.configure(bg=COLOR_BACKGROUND)  # 窗口背景颜色
        self.ROOT.iconbitmap('icon.ico')  # 设置窗口图标

        # 限制Entry只能输入数字
        self.OnlyNumber = self.ROOT.register(only_number)  # 将函数包装一下

        # --No.1--↓↓↓↓↓ 绘制window基本框架-----------------
        self.CNTL_Menu_BTN = []  # 存储菜单按钮控件对象，用于点击后改变按钮状态
        self.CNTL_NowFace = None
        self.window_layout()
        # --No.1--↑↑↑↑↑ 绘制window基本框架-----------------

        # --No.2--↓↓↓↓↓ 必要初始化判断-----------------
        init_ready()
        # --No.2--↑↑↑↑↑ 必要初始化判断-----------------

        # --No.3--↓↓↓↓↓ 通用图片初始化-----------------
        # --页面所需图片变量，所有需要的图片变量放至 window 下，防止不显示
        self.tkImage = {
            'currency': []
        }
        # --初始化所有页面都用到的图片
        self.NeedImgPath = [
            'skin/Currency/Internet/networkerror.png',
            'skin/Currency/Internet/timeout.png'
        ]
        self.imginit('currency')  # 初始化所需通用图片
        # --No.3--↑↑↑↑↑ 通用图片初始化-----------------

        # -----↓↓↓↓↓↓↓↓↓↓ 初始化变量 ↓↓↓↓↓↓↓↓↓↓-----
        self.NowFaceCode = 0  # 当前处在什么页面, 更改该值，即由此进入程序时的页面
        # self.NowFaceCode可选值: int：0 选股；1 持有；2 历史
        # 选股分析线程
        self.tdResult_Choice = None  # 存储选股分析的线程结果
        self.tdObject_Choice = None  # 存储选股分析的线程对象
        # 持有页面定时刷新
        self.monitorState_Hold = False  # Hold的监听器开启标志位
        self.tdResult_Hold = None  # 存储持有数据的线程结果
        self.tdObject_Hold = None  # 存储刷新持有数据线程对象
        # -----↑↑↑↑↑↑↑↑↑↑ 初始化变量 ↑↑↑↑↑↑↑↑↑↑-----

        # --No4--↓↓↓↓↓ 更新持有的最新价格-----------------
        self.td_run_hold()
        # --No4--↓↓↓↓↓ 更新持有的最新价格-----------------

        # --No.Last--↓↓↓↓↓ 绘制初始页面----------------
        self.event_switch_face(self.NowFaceCode)
        # --No.Last--↑↑↑↑↑ 绘制初始页面----------------

    def td_run_hold(self):
        """执行更新持有最新价线程"""

        self.tdObject_Hold = MyThread(Ldb.update_hold)
        self.tdObject_Hold.setDaemon(True)  # 设为主线程的守护线程，主线程结束，该线程结束！
        self.tdObject_Hold.start()  # 开始线程

    def window_layout(self):
        # --绘制导航---------------
        # 导航菜单框架
        # --master self.ROOT 主窗口
        frame_menu_attrs = {
            'bg': COLOR_THEME,
        }
        frame_menu_place = {
            'width': SIZE_MENU_W,
            'height': SIZE_MENU_H,
        }
        frame_menu = tk.Frame(
            self.ROOT,
            frame_menu_attrs
        )
        frame_menu.place(frame_menu_place)
        # 导航按钮
        # --master frame_menu 导航菜单框架
        button_menu_text = ("选股", "持有", "历史")
        button_menu_attrs = {
            'bg': frame_menu_attrs['bg'],  # 'red',
            'bd': 0,  # 去掉边框
            'fg': COLOR_BTNFG,
            'font': FONT_MENU,
            'activebackground': frame_menu_attrs['bg'],  # 点击时的背景色与菜单框架一致
            'activeforeground': 'white',
        }
        for index in range(len(button_menu_text)):
            button_menu_attrs['text'] = button_menu_text[index]
            button_menu = tk.Button(
                frame_menu,
                button_menu_attrs
            )
            button_menu.pack(side='left', padx=10)
            # 绑定导航菜单按钮事件
            button_menu.configure(command=lambda pre=index: self.event_switch_face(pre))
            self.CNTL_Menu_BTN.append(button_menu)
        # --绘制导航---------------

        # --绘制桌面框架------------
        # --master self.ROOT 主窗口
        frame_face_attrs = FRAME_ATTRS.copy()
        frame_face_place = {
            'width': SIZE_FACE_W,
            'height': SIZE_FACE_H,
            'y': frame_menu_place['height']
        }
        self.CNTL_NowFace = tk.Frame(
            self.ROOT,
            frame_face_attrs
        )
        self.CNTL_NowFace.place(frame_face_place)
        # --绘制桌面框架------------

    def imginit(self, key: str):
        """初始化页面需要的图片"""
        lackRoute = ''  # 存储缺少的图片信息
        for route in self.NeedImgPath:
            try:
                content = SKINZIP.read(route)
                # 临时存储图片
                tempimg = open('tempImg.png', 'wb+')
                tempimg.write(content)  # 将图片写入临时图片
                tempimg.close()
                # 设置tk可用img
                self.tkImage[key].append(tk.PhotoImage(file='tempImg.png'))
                # 删除临时图片
                os.remove('tempImg.png')
            except KeyError:
                print(f'{route} 在Skin.zip中不存在！')
                lackRoute += route + '\n'
        if lackRoute:
            messagebox.showerror(
                title='文件损坏',
                message=f'必要文件损坏，请检查文件 ‘Skin.zip’：\n{lackRoute}'
            )
            exit()
        # 清空路径列表，释放内存
        self.NeedImgPath = []

    def draw_errors(self, master, state):
        """
        绘制访问超时页面

        :param master: 父级框架
        :param state: 错误状态，'Time out'-超时 'Network error'-网络错误 'None data'
        :return:
        """

        # 信息显示框架
        frame_errorinfo_attrs = FRAME_ATTRS.copy()
        # frame_errorinfo_attrs['bg'] = 'green'
        frame_errorinfo_place = {
            'width': SIZE_TBODY_W,
            'height': SIZE_TBODY_W,
        }
        frame_errorinfo = tk.Frame(
            master,
            frame_errorinfo_attrs
        )
        frame_errorinfo.place(frame_errorinfo_place)

        # 图片显示框架
        # --master frame_errorinfo 信息显示框架
        label_errorimg_attrs = {
            'bg': COLOR_BACKGROUND,
        }

        # 判断显示的图片和文字
        if state == 'Time out':
            label_errorimg_attrs['image'] = self.tkImage['currency'][1]  # 超时图片
            text = '网络连接超时！'
        elif state == 'Network error':
            label_errorimg_attrs['image'] = self.tkImage['currency'][0]  # 网络错误图片
            text = '网络连接错误！'
        elif state == 'None data':
            tk.Label(
                master,
                bd=0,
                bg=COLOR_BACKGROUND,
                fg=COLOR_TIPS_TALL,
                font=FONT_NONEDATA_ABNORMAL,
                text='暂无数据！',
                anchor='n',
            ).pack(expand='yes', fill='both')
            return None
        else:
            text = '未知错误！'

        label_errorimg = tk.Label(
            frame_errorinfo,
            label_errorimg_attrs,
        )
        label_errorimg.pack()
        # 文字显示框架
        # --master frame_errorinfo 信息显示框架
        label_errorfont_attrs = {
            'bg': COLOR_BACKGROUND,
            'font': FONT_NONEDATA_ABNORMAL,
            'fg': COLOR_TIPS_TALL,
            'text': text
        }
        label_errorfont = tk.Label(
            frame_errorinfo,
            label_errorfont_attrs
        )
        label_errorfont.pack()

    def destory_face(self):
        """清空桌面，方便重新绘制"""
        for widget in self.CNTL_NowFace.winfo_children():
            widget.destroy()

    def event_switch_face(self, face_code):
        """
        持有、历史、选股页面的切换

        :param face_code: int 页面代码：0 选股；1 持有；2 历史
        """
        # 变换导航按钮样式
        for i in range(len(self.CNTL_Menu_BTN)):
            if i == face_code:
                self.CNTL_Menu_BTN[i]["fg"] = COLOR_BTNFG_ACTIVE
            else:
                self.CNTL_Menu_BTN[i]["fg"] = COLOR_BTNFG

        if face_code == 0:
            self.NowFaceCode = 0  # 当前处在持有页面
            # print('选股页面')
            ChoiceFace(self)

        elif face_code == 1:
            self.NowFaceCode = 1  # 当前处在历史页面
            # print('持有页面')
            HoldFace(self)

        elif face_code == 2:
            self.NowFaceCode = 2  # 当前处在选股页面
            # print('持有历史页面')
            HoldHistoryFace(self)


def event_business(code_oder):
    """
    模拟买卖事件

    :param code_oder: 买卖参数。
            str - 模拟买入
            int - 模拟卖出
    :return:
        【True】 - 卖出成功
        【False】 - 卖出失败
    """
    title = '错误'
    # 校准时间
    calibration_time()
    if not isinstance(NETTIME_STAMP, int):
        # 校准网络时间失败
        info = NETTIME_STAMP
    else:
        if isinstance(code_oder, str):
            info = Ldb.buy(code_oder, NETTIME)
            title = '模拟买入'
        elif isinstance(code_oder, int):
            info = Ldb.sale(code_oder, NETTIME)
            title = '模拟卖出'
        else:
            print(f'event_business()参数错误：{code_oder}')
            return False

    if not isinstance(info, dict):
        if info == 'Held':
            message = f'{code_oder} 已持有！'
        elif info == 'TryAll error':
            message = f'{code_oder} 股票代码错误！'
        elif info == 'Time out':
            message = '请求超时，请稍后重试！'
        elif info == 'Network error':
            message = '网络错误，请检查网络后重试！'
        elif info == 'None data':
            message = '数据不存在！'
        elif info == 'Not Held':
            message = f'卖出失败，Oder:{code_oder} 已模拟卖出！'
        else:
            message = '未知错误!'
        messagebox.showerror(
            title='错误',
            message=message
        )
        return False
    # 确定询问
    userchoose = messagebox.askokcancel(title=title,
                                        message=info['Tips']
                                        )
    # 判断用户是否确定
    if userchoose:
        # 执行更新语句
        s = Ldb.myexecute(info['SQL'])
        if s:
            messagebox.showerror(
                title='错误',
                message=s
            )
            return False
        else:
            return True


class FACE:
    def __init__(self, window):
        self.window = window  # 主窗口类
        window.destory_face()  # 清空桌面
        # --↓↓↓↓↓ 初始化变量 ↓↓↓↓↓--
        # face页面所需图片
        self.window.tkImage['face'] = []

        # --↑↑↑↑↑ 初始化变量 ↑↑↑↑↑--

    def event_business(self, code_oder):
        """
        模拟买卖事件

        :return:
        """

        if event_business(code_oder):
            print('购买成功')
            # 刷新整个页面
            self.refresh()
        else:
            print('购买失败')
            return None


class ChoiceFace(FACE):
    def __init__(self, window):
        FACE.__init__(self, window)
        # --初始化本页面需要的图片
        self.window.tkImage['ChoiceFace'] = []
        # --页面都用到的图片
        self.window.NeedImgPath = [
            'skin/Choices/ImgButton/search.png',
        ]
        self.window.imginit('ChoiceFace')  # 初始化所需通用图片

        # --加载动画相关的变量
        self.LoadingTextIndex = 0  # 高亮字体计数
        self.LoadingText = ('数', '据', '分', '析', '中', '.', '.', '.')  # 高亮字体
        self.LoadingText_LEN = len(self.LoadingText)
        self.LoadingSpeed = 200  # 动画的速度(ms)

        # --模式选择相关变量
        self.NowMode = 0  # 在此设置，即初始绘制的模式选择页面
        # self.NowMode可选值：0 按照基金排行；1 按照基金名称；2 按照股票代码
        self.Text_Type = ('全部', '股票', '混合', '指数')  # '基金类型'
        self.Text_TimeSlot = ('近1周', '近1月', '近3月', '近6月', '近1年', '近3年')  # '时间段'
        self.Text_Rank = ('前30', '前50', '前100', '前200')  # '排名'
        self.PREs_byjjrank = {
            '基金类型': ('all', 'gp', 'hh', 'zs'),
            '时间段': ('zzf', '1yzf', '3yzf', '6yzf', '1nzf', '3nzf'),
            '排名': ('30', '50', '100', '200'),
        }
        self.PRE_byjjrank = {
            '基金类型': '',
            '时间段': '',
            '排名': '',
        }  # 存放按基金排名的参数，如果未 '' 说明该参数未选择
        self.CNTL_RecomModeBTN = []  # 按钮控件对象，用于改变按钮状态
        self.CNTL_FrameOption = None  # 存放模式选项控件的框架对象
        self.CNTL_OptionBTN = {
            '基金类型': [],  # 基金类型，如：全部、股票、指数等
            '时间段': [],  # 哪个时间段，如 近1周
            '排名': [],  # 排名，如 前50
        }  # 存放“按基金排名”选项的控件对象

        self.StringVar_SearchEntry = tk.StringVar()  # 存储搜索输入框框的值

        # 监听结果
        self.monitor_choice_tdresult()

    def monitor_choice_tdresult(self):
        """
        监听线程结果，根据线程结果，给出不同页面

        :return:
        """
        #   线程结果变量存储在 window 中，为了达到分析一次后，将分许结果存储在内存，
        # 只要不关闭 window 页面或用户不主动清除，分析的数据就不会被清除。
        #   self.window.tdResult_Choice 可分为：
        # None - 无线程
        # 'Not started' - 线程未启动
        # 'Started' - 线程已启动，未结束
        # dict - 字典，表示数据分析完成

        # 只有页面处于选股页面才开启监听
        if self.window.NowFaceCode == 0:
            print(f'处于选股页面！')
            # 判断是否有结果
            if isinstance(self.window.tdResult_Choice, dict):
                # 有分析数据。绘制结果
                print(f'分析完成，绘制结果！')
                ChoiceResult(self.window)
            else:
                # 没有结果，判断是否有线程
                if not self.window.tdObject_Choice:
                    print('无分析结果，并且无 Choice 线程对象，绘制选股选项页面！')
                    # 绘制选股选项页面
                    self.draw_choiceface()
                else:
                    # 有线程，获取线程结果
                    self.window.tdResult_Choice = self.window.tdObject_Choice.get_result()

                    if not isinstance(self.window.tdResult_Choice, dict):
                        print(f'当前 Choice 线程状态：{self.window.tdResult_Choice}')
                        # 如果返回结果不是字典，可能线程未启动或已启动未结束！绘制loading
                        self.analysis_loading_animation()

                    # 继续监听
                    self.window.ROOT.after(self.LoadingSpeed, self.monitor_choice_tdresult)

    def analysis_loading_animation(self):
        """
        分析中动画效果

        :return:
        """
        frame_loadcenter_attrs = FRAME_ATTRS.copy()
        # frame_loadcenter_attrs['bg'] = 'blue'
        frame_loadcenter_attrs['width'] = SIZE_FACE_W
        frame_loadcenter_attrs['height'] = SIZE_FACE_H
        frame_loadcenter_place = {
            'width': SIZE_FACE_W,
            'height': SIZE_FACE_H,
        }
        frame_loadcenter = tk.Frame(
            self.window.CNTL_NowFace,  # 整个动画基于整个FACE
            frame_loadcenter_attrs
        )
        frame_loadcenter.place(frame_loadcenter_place)
        # 整合居中框架
        # --master self.window.NowFace_cntl 桌面框架
        frame_allloadcenter_attrs = FRAME_ATTRS.copy()
        # frame_allloadcenter_attrs['bg'] = 'red'
        # frame_allloadcenter_attrs['width'] = 100
        # frame_allloadcenter_attrs['height'] = 50
        frame_allloadcenter = tk.Frame(
            frame_loadcenter,
            frame_allloadcenter_attrs
        )
        frame_allloadcenter.pack(side='top', expand='yes')
        # label
        # --master frame_allloadcenter 居中框架
        label_load_attrs = {
            'bg': COLOR_BACKGROUND,
            'font': FONT_LOADING,
        }
        for index in range(self.LoadingText_LEN):
            if index == self.LoadingTextIndex:
                # 高亮的字
                label_load_attrs['fg'] = COLOR_THEME
            else:
                label_load_attrs['fg'] = COLOR_TIPS_LOW

            label_load_attrs['text'] = self.LoadingText[index]
            label_load = tk.Label(
                frame_allloadcenter,
                label_load_attrs
            )
            label_load.pack(side='left', expand='yes', padx=5)

        if self.LoadingTextIndex == self.LoadingText_LEN - 1:
            # 重置
            self.LoadingTextIndex = 0
        else:
            # 下一个高亮字体
            self.LoadingTextIndex += 1

    def draw_choiceface(self):
        """
        绘制选股选项页面

        :return:
        """

        # 选股标题
        # --master self.window.CNTL_NowFace 桌面框架
        label_chiocetitle_attrs = {
            'text': 'Choices',
            'anchor': 's',  # 显示在底部
            'font': FONT_CHOICETITLE,
            'fg': COLOR_THEME,
            'bg': COLOR_BACKGROUND,
        }
        label_chiocetitle_place = {
            'width': SIZE_CHOICETITLE_W,
            'height': SIZE_CHOICETITLE_H,
        }
        label_chiocetitle = tk.Label(
            self.window.CNTL_NowFace,
            label_chiocetitle_attrs
        )
        label_chiocetitle.place(label_chiocetitle_place)

        # 模式选择按钮框架--------------------------------------
        # --master self.window.CNTL_NowFace 桌面框架
        frame_recommode_attrs = FRAME_ATTRS.copy()
        # frame_recommode_attrs['bg'] = 'pink'
        frame_recommode_place = {
            'width': SIZE_CHOICE_RECOMMODE_W,
            'height': SIZE_CHOICE_RECOMMODE_H,
            'x': 0,
            'y': SIZE_CHOICETITLE_H,
        }
        frame_recommode = tk.Frame(
            self.window.CNTL_NowFace,
            frame_recommode_attrs
        )
        frame_recommode.place(frame_recommode_place)
        # 为了控件能再一行居中显示，再套一层居中的框架
        # 模式选择居中框架
        # --master frame_recommode 模式选择按钮框架
        frame_rcmodecenter_attrs = FRAME_ATTRS.copy()
        # frame_rcmodecenter_attrs['bg'] = 'yellow'
        frame_rcmodecenter = tk.Frame(
            frame_recommode,
            frame_rcmodecenter_attrs
        )
        frame_rcmodecenter.pack()
        # 模式选择按钮
        # --master frame_rcmodecenter 模式选择居中框架
        mode_text = ('按基金排名', '按基金名称', '按股票代码')
        # 推荐模式选择按钮
        button_recommode_attrs = {
            'bg': COLOR_BACKGROUND,  # 'green',
            'font': FONT_RECOMMODE,
            'fg': COLOR_THEME,
            # 'width': 10,
            "relief": "solid",
            'bd': 1,
            "activebackground": COLOR_BACKGROUND,
            "activeforeground": COLOR_THEME,
            'disabledforeground': 'white',  # 禁用时的按钮文本颜色
        }
        for index in range(len(mode_text)):
            button_recommode_attrs['text'] = mode_text[index]
            button_recommode = tk.Button(
                frame_rcmodecenter,
                button_recommode_attrs
            )
            button_recommode.pack(side='left', expand='yes', padx=20, ipadx=10)
            self.CNTL_RecomModeBTN.append(button_recommode)  # 存储推荐模式按钮对象
            # 绑定按钮事件
            button_recommode.configure(command=lambda pre=index: self.event_switch_mode(pre))
        # 模式选择按钮框架--------------------------------------

        # 推荐模式选项框架--------------------------------------
        # --master self.window.CNTL_NowFace 桌面框架
        frame_option_attrs = FRAME_ATTRS.copy()
        # frame_option_attrs['bg'] = 'blue'
        frame_option_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': SIZE_CHOICE_RECOMMODE_FACE_H,
            'y': SIZE_CHOICETITLE_H + SIZE_CHOICE_RECOMMODE_H
        }
        self.CNTL_FrameOption = tk.Frame(
            self.window.CNTL_NowFace,
            frame_option_attrs
        )
        self.CNTL_FrameOption.place(frame_option_place)
        # 推荐模式选项框架--------------------------------------

        # 绘制初始模式
        self.event_switch_mode(self.NowMode)

    def draw_byjjrank(self):
        """
        绘制按基金排名选股的选项

        :return:
        """
        # 选项的一行高度
        alineheight = SIZE_CHOICE_RECOMMODE_FACE_H * 0.15
        pady = 20
        # 按钮属性
        button_attrs = {
            'bg': COLOR_BACKGROUND,  # 'green',
            'font': FONT_RECOMMODE_OPTION,
            'fg': COLOR_THEME,
            "relief": "solid",
            'bd': 1,
            "activebackground": COLOR_BACKGROUND,
            "activeforeground": COLOR_THEME,
            'disabledforeground': 'white',  # 禁用时的按钮文本颜色
            'cursor': 'dotbox',
        }

        # 基金类型选项框架
        # --master self.CNTL_Option 选股选项框架
        frame_jjtype_attrs = FRAME_ATTRS.copy()
        # frame_jjtype_attrs['bg'] = 'yellow'
        frame_jjtype_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': alineheight,
        }
        frame_jjtype = tk.Frame(
            self.CNTL_FrameOption,
            frame_jjtype_attrs
        )
        frame_jjtype.place(frame_jjtype_place)
        # 居中框架
        # --master frame_recommode 模式选择按钮框架
        frame_center_attrs = FRAME_ATTRS.copy()
        frame_center = tk.Frame(
            frame_jjtype,
            frame_center_attrs
        )
        frame_center.pack()
        # 类型选项-----------------------------------------------------
        # --master frame_center 居中框架
        button_type_attrs = button_attrs.copy()
        self.CNTL_OptionBTN['基金类型'] = []  # 先重置，防止反复添加
        for index in range(len(self.Text_Type)):
            button_type_attrs['text'] = self.Text_Type[index]
            button_type = tk.Button(
                frame_center,
                button_type_attrs
            )
            button_type.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
            self.CNTL_OptionBTN['基金类型'].append(button_type)
            # 绑定事件
            button_type.configure(
                command=lambda pre='基金类型', pre2=index: self.event_clickrankoption(pre, pre2)
            )
        # 类型选项-----------------------------------------------------

        # 时间段选项框架
        # --master self.CNTL_Option 选股选项框架
        frame_jjtimeslot_attrs = FRAME_ATTRS.copy()
        # frame_jjtimeslot_attrs['bg'] = 'pink'
        frame_jjtimeslot_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': alineheight,
            'y': frame_jjtype_place['height']
        }
        frame_jjtimeslot = tk.Frame(
            self.CNTL_FrameOption,
            frame_jjtimeslot_attrs
        )
        frame_jjtimeslot.place(frame_jjtimeslot_place)
        # 居中框架
        # --master frame_jjtimeslot 时间段选项框架
        frame_center_attrs = FRAME_ATTRS.copy()
        frame_center = tk.Frame(
            frame_jjtimeslot,
            frame_center_attrs
        )
        frame_center.pack()
        # 时间段选项-----------------------------------------------------
        # --master frame_center 居中框架
        button_timeslot_attrs = button_attrs.copy()
        self.CNTL_OptionBTN['时间段'] = []  # 先重置，防止反复添加
        for index in range(len(self.Text_TimeSlot)):
            button_timeslot_attrs['text'] = self.Text_TimeSlot[index]
            button_timeslot = tk.Button(
                frame_center,
                button_timeslot_attrs
            )
            button_timeslot.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
            self.CNTL_OptionBTN['时间段'].append(button_timeslot)
            # 绑定事件
            button_timeslot.configure(
                command=lambda pre='时间段', pre2=index: self.event_clickrankoption(pre, pre2)
            )
        # 时间段选项-----------------------------------------------------

        # 排名前多少名选项框架
        # --master self.CNTL_Option 选股选项框架
        frame_jjrank_attrs = FRAME_ATTRS.copy()
        # frame_jjrank_attrs['bg'] = 'green'
        frame_jjrank_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': alineheight,
            'y': frame_jjtimeslot_place['y'] + frame_jjtimeslot_place['height']
        }
        frame_jjrank = tk.Frame(
            self.CNTL_FrameOption,
            frame_jjrank_attrs
        )
        frame_jjrank.place(frame_jjrank_place)
        # 居中框架
        # --master frame_jjrank 排名前多少名选项框架
        frame_center_attrs = FRAME_ATTRS.copy()
        frame_center = tk.Frame(
            frame_jjrank,
            frame_center_attrs
        )
        frame_center.pack()
        # 排行选项-----------------------------------------------------
        # --master frame_center 居中框架
        button_rank_attrs = button_attrs.copy()
        self.CNTL_OptionBTN['排名'] = []  # 先重置，防止反复添加
        for index in range(len(self.Text_Rank)):
            button_rank_attrs['text'] = self.Text_Rank[index]
            button_rank = tk.Button(
                frame_center,
                button_rank_attrs
            )
            button_rank.pack(side='left', expand='yes', padx=20, pady=pady, ipadx=10)
            self.CNTL_OptionBTN['排名'].append(button_rank)
            # 绑定事件
            button_rank.configure(
                command=lambda pre='排名', pre2=index: self.event_clickrankoption(pre, pre2)
            )
        # 排行选项-----------------------------------------------------

        # 确定按钮框架
        # --master self.CNTL_Option 选股选项框架
        frame_det_attrs = FRAME_ATTRS.copy()
        # frame_det_attrs['bg'] = 'red'
        frame_det_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': alineheight,
            'y': frame_jjrank_place['y'] + (frame_jjrank_place['height'] * 1.5)
        }
        frame_jjrank = tk.Frame(
            self.CNTL_FrameOption,
            frame_det_attrs
        )
        frame_jjrank.place(frame_det_place)
        # 居中框架
        # --master frame_jjrank 排名前多少名选项框架
        frame_center_attrs = FRAME_ATTRS.copy()
        frame_center = tk.Frame(
            frame_jjrank,
            frame_center_attrs
        )
        frame_center.pack()
        # 确定按钮-----------------------------------------------------
        # --master frame_center 居中框架
        button_dat_attrs = BUTTON_ATTRS.copy()
        button_dat_attrs['bd'] = 0
        button_dat_attrs['font'] += ' underline'
        button_dat_attrs['text'] = '确定'
        button_dat = tk.Button(
            frame_center,
            button_dat_attrs
        )
        button_dat.pack(side='bottom', expand='yes')
        # 绑定事件
        button_dat.configure(
            command=lambda: self.event_ok_byjjrank()
        )
        # 确定按钮-----------------------------------------------------

    def draw_byjjname(self):
        """
        绘制按基名称选股的选项

        :return:
        """
        # 名称搜索输入框按钮框架
        # --master self.CNTL_FrameOption 选项输入框
        frame_jjtype_attrs = FRAME_ATTRS.copy()
        # frame_jjtype_attrs['bg'] = 'green'
        frame_jjtype_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': SIZE_CHOICE_RECOMMODE_FACE_H * 0.3,
            'x': 0,
            'y': 0,
        }
        frame_jjtype = tk.Frame(
            self.CNTL_FrameOption,
            frame_jjtype_attrs
        )
        frame_jjtype.place(frame_jjtype_place)
        # 居中框架
        # --master frame_jjtype 输入框框架
        frame_jjtypecenter_attrs = FRAME_ATTRS.copy()
        # frame_jjtypecenter_attrs['bg'] = 'pink'
        frame_jjtypecenter = tk.Frame(
            frame_jjtype,
            frame_jjtypecenter_attrs
        )
        frame_jjtypecenter.pack(side='top', expand='yes')
        # 输入框画布
        # --master frame_jjtypecenter
        line_widght = 2  # 输入框线条宽度
        line_color = 'black'  # 输入框线条颜色
        canvas_entry_attrs = CANVAS_ATTRS.copy()
        # canvas_entry_attrs['bg'] = 'blue'
        canvas_entry_attrs['width'] = frame_jjtype_place['width'] * 0.4  # frame_jjtype_attrs['width'] * 0.3
        canvas_entry_attrs['height'] = frame_jjtype_place['height'] * 0.4  # frame_jjtype_attrs['height'] * 0.4
        canvas_entry = tk.Canvas(
            frame_jjtypecenter,
            canvas_entry_attrs
        )
        # 绘制输入框下的横线，
        draw_tb_line(canvas_entry, canvas_entry_attrs, tt=(0, 1), line_wight=line_widght, line_color=line_color)
        canvas_entry.pack(side='left', expand='no', padx=3)
        # 输入框
        # --master canvas_entry 输入框画布，画布用来绘制横线
        entry_name_attrs = {
            'bg': COLOR_BACKGROUND,
            'bd': 0,
            'font': FONT_ENTRY,
            'fg': COLOR_TIPS_TALL,
            'justify': 'center',
            'textvariable': self.StringVar_SearchEntry,
        }
        entry_name_place = {
            'width': canvas_entry_attrs['width'],
            'height': canvas_entry_attrs['height'] - line_widght,
        }
        entry_name = tk.Entry(
            canvas_entry,
            entry_name_attrs
        )
        entry_name.place(entry_name_place)
        # 搜索按钮
        # --master frame_jjtype
        # button_entry_attrs = BUTTON_ATTRS.copy()
        self.StringVar_SearchEntry.set("")  # 重置值
        button_entry_attrs = {
            'bd': 0,
            'bg': COLOR_BACKGROUND,
            "activebackground": COLOR_BACKGROUND,
            'image': self.window.tkImage['ChoiceFace'][0],
        }
        button_entry = tk.Button(
            frame_jjtypecenter,
            button_entry_attrs
        )
        button_entry.pack(side='left', expand='no', fill='both', padx=5)
        # 绑定事件
        button_entry.configure(
            command=lambda pre='byjjname': self.event_search_btn(pre)
        )

    def draw_bystockcode(self):
        """
        按照股票代码

        :return:
        """

        # 名称搜索输入框按钮框架
        # --master self.CNTL_FrameOption 选项输入框
        frame_jjtype_attrs = FRAME_ATTRS.copy()
        # frame_jjtype_attrs['bg'] = 'green'
        frame_jjtype_place = {
            'width': SIZE_CHOICE_RECOMMODE_FACE_W,
            'height': SIZE_CHOICE_RECOMMODE_FACE_H * 0.3,
        }
        frame_jjtype = tk.Frame(
            self.CNTL_FrameOption,
            frame_jjtype_attrs
        )
        frame_jjtype.place(frame_jjtype_place)
        # 居中框架
        # --master frame_jjtype 输入框框架
        frame_jjtypecenter_attrs = FRAME_ATTRS.copy()
        # frame_jjtypecenter_attrs['bg'] = 'pink'
        frame_jjtypecenter = tk.Frame(
            frame_jjtype,
            frame_jjtypecenter_attrs
        )
        frame_jjtypecenter.pack(side='top', expand='yes')
        # 输入框画布
        # --master frame_jjtypecenter
        line_widght = 2  # 输入框线条宽度
        line_color = 'black'  # 输入框线条颜色
        canvas_entry_attrs = CANVAS_ATTRS.copy()
        # canvas_entry_attrs['bg'] = 'blue'
        canvas_entry_attrs['width'] = frame_jjtype_place['width'] * 0.4  # frame_jjtype_attrs['width'] * 0.3
        canvas_entry_attrs['height'] = frame_jjtype_place['height'] * 0.4  # frame_jjtype_attrs['height'] * 0.4
        canvas_entry = tk.Canvas(
            frame_jjtypecenter,
            canvas_entry_attrs
        )
        # 绘制输入框下的横线，
        draw_tb_line(canvas_entry, canvas_entry_attrs, tt=(0, 1), line_wight=line_widght, line_color=line_color)
        canvas_entry.pack(side='left', expand='no', padx=3)
        # 输入框
        # --master canvas_entry 输入框画布，画布用来绘制横线
        entry_name_attrs = {
            'bg': COLOR_BACKGROUND,
            'bd': 0,
            'font': FONT_ENTRY,
            'fg': COLOR_TIPS_TALL,
            'justify': 'center',
            'textvariable': self.StringVar_SearchEntry,
            'validate': "key",
            'validatecommand': (self.window.OnlyNumber, '%P'),  # 显示entry只能输入数字
        }
        entry_name_place = {
            'width': canvas_entry_attrs['width'],
            'height': canvas_entry_attrs['height'] - line_widght,
        }
        entry_name = tk.Entry(
            canvas_entry,
            entry_name_attrs
        )
        entry_name.place(entry_name_place)
        # 搜索按钮
        # --master frame_jjtype
        # button_entry_attrs = BUTTON_ATTRS.copy()
        self.StringVar_SearchEntry.set("")  # 重置值
        button_entry_attrs = {
            'bd': 0,
            'bg': COLOR_BACKGROUND,
            "activebackground": COLOR_BACKGROUND,
            'image': self.window.tkImage['ChoiceFace'][0],
        }
        button_entry = tk.Button(
            frame_jjtypecenter,
            button_entry_attrs
        )
        button_entry.pack(side='left', expand='no', fill='both', padx=5)
        # 绑定事件
        button_entry.configure(
            command=lambda pre='bystockcode': self.event_search_btn(pre)
        )

    def td_run(self, function, param):
        """
        开启分析线程

        :param function: 线程函数
        :param param: 函数的参数
        :return:
        """
        self.window.tdObject_Choice = MyThread(function, param)
        self.window.tdObject_Choice.setDaemon(True)  # 设为主线程的守护线程，主线程结束，该线程结束！
        self.window.tdObject_Choice.start()  # 开始线程

        # 开启监听
        self.monitor_choice_tdresult()

    def event_ok_byjjrank(self):
        """
        按基金排名，确定按钮事件

        :return:
        """
        # print(f'选择的参数：{self.PRE_byjjrank}')
        # 判断哪个参数未选择
        message = '请选择：'
        for par, value in self.PRE_byjjrank.items():
            if not value:
                message += f'”{par}“，'
        if message != '请选择：':
            # 有未选选项
            message = message[:-1] + '。'  # 去掉多余逗号加上句号
            messagebox.showwarning(title='请选择', message=message)
        else:
            # 处理参数
            byjjrank_param = self.PRE_byjjrank.values()
            # 开始进行数据分析
            self.td_run(td_byjjrank, byjjrank_param)

    def event_search_btn(self, mode):
        """
        按基金名称，股票代码搜索按钮事件

        :return:
        """
        # 获取输入值
        input_content = self.StringVar_SearchEntry.get()
        if not input_content:
            # 无输入
            return None
        # 判断模式
        if mode == 'byjjname':
            self.td_run(td_byjjname, (input_content,))

        if mode == 'bystockcode':
            if len(input_content) > 6:
                messagebox.showerror(
                    title='输入错误', message='请输入 6 位以内的数字！'
                )
                self.StringVar_SearchEntry.set("")
                return None
            # 绘制个股
            StockInfo(self.window, input_content)

    def event_clickrankoption(self, key: str, index):
        """
        按基金排名，选项按钮点击事件

        :param key: 哪一项
        :param index: 点击的哪个按钮
        :return:
        """
        try:
            terms = self.CNTL_OptionBTN[key]
        except KeyError:
            print('按钮绑定事件，传参错误！')
            return None

        # 先重置所有控件状态
        for indv in terms:
            indv['bg'] = COLOR_BACKGROUND
            indv['fg'] = COLOR_THEME
            indv['state'] = 'normal'

        # 改变点击控件状态
        terms[index]['bg'] = COLOR_THEME
        terms[index]['fg'] = 'white'
        terms[index]['state'] = 'disable'
        # 更改参数下标
        self.PRE_byjjrank[key] = self.PREs_byjjrank[key][index]

    def event_switch_mode(self, mode):
        """
        模式选择按钮绑定事件

        :return:
        """
        # 先清空选项框架内容
        for widget in self.CNTL_FrameOption.winfo_children():
            widget.destroy()
        # 重置按钮状态
        for cntl in self.CNTL_RecomModeBTN:
            cntl['bg'] = COLOR_BACKGROUND
            cntl['fg'] = COLOR_THEME
            cntl['state'] = 'normal'
        # 改变按钮状态
        self.CNTL_RecomModeBTN[mode]['bg'] = COLOR_THEME
        # normal
        # disabled
        # active
        self.CNTL_RecomModeBTN[mode]['state'] = 'disabled'  # 禁用按钮
        # self.CNTL_RecomModeBTN[self.NowMode]['fg'] = 'white'

        self.NowMode = mode
        if self.NowMode == 0:
            # print(f'按基金排行')
            self.draw_byjjrank()  # 按基金排行
        elif self.NowMode == 1:
            # print(f'按基金名称')
            self.draw_byjjname()
        elif self.NowMode == 2:
            # print(f'按股票代码')
            self.draw_bystockcode()


class StockInfo(FACE):
    def __init__(self, window, code, sortcol='bcynum', sortmode='desc', nowpagenum=0):
        """
        绘制个股信息

        :param window:
        :param code: 股票代码
        :param sortcol: 非直接进入此页面，上级页面的排序列
        :param sortmode: 非直接进入此页面，上级页面的排序方式
        :param nowpagenum: 非直接进入此页面，上级页面的页数
        """
        FACE.__init__(self, window)
        # --初始化本页面需要的图片
        self.window.tkImage['StockInfo'] = []

        print(f'{code} 个股信息页面')
        self.Code = code  # 股票代码
        # 如果是从非选股页面进入个股详情，以下参数会用到。达到返回原来页面
        self.SortColumn = sortcol
        self.SortMode = sortmode
        self.NowPageNum = nowpagenum

        # --本页面框架结构--------
        self.CNTL_Stock = {}
        # {
        #   'title':  # 标题
        #   'body':  # 主体
        # }
        # --本页面框架结构--------

        # 动态显示个股信息
        self.StringVar_DetailedInfo = {}

        # 绘制初始框架
        self.stockinfo_layout()
        # 刷新页面
        self.refresh()

    def stockinfo_layout(self):
        """
        个股信息页面初始布局

        :return:
        """
        # 标题框架
        # --master self.window.CNTL_NowFace 桌面框架
        frame_title_attrs = CANVAS_ATTRS.copy()
        # frame_title_attrs['bg'] = 'red'
        frame_title_place = {
            'width': SIZE_STOCKFACE_TITLE_W,
            'height': SIZE_STOCKFACE_TITLE_H,
            'x': SIZE_STOCKFACE_MARGIN
        }
        self.CNTL_Stock['title'] = tk.Canvas(
            self.window.CNTL_NowFace,
            frame_title_attrs
        )
        self.CNTL_Stock['title'].place(frame_title_place)
        draw_tb_line(self.CNTL_Stock['title'], frame_title_place, tt=(0, 1))

        # 页面内容主体
        # --master self.window.CNTL_NowFace 桌面框架
        frame_content_attrs = FRAME_ATTRS.copy()
        frame_content_attrs['bg'] = 'pink'
        frame_content_place = {
            'width': SIZE_STOCKFACE_CONTENT_W,
            'height': SIZE_STOCKFACE_CONTENT_H,
            'x': frame_title_place['x'],
            'y': frame_title_place['height']
        }
        self.CNTL_Stock['content'] = tk.Frame(
            self.window.CNTL_NowFace,
            frame_content_attrs
        )
        self.CNTL_Stock['content'].place(frame_content_place)

    def destroy_content(self):
        # 清空内容框架
        for widget in self.CNTL_Stock['content'].winfo_children():
            widget.destroy()

    def refresh(self):
        """
        刷新当前页面

        :return:
        """
        # 绘制标题内容
        self.draw_title()
        # 获取最新数据
        self.handle_data()

    def handle_data(self):
        """
        获取个股信息最新数据

        :return:
        """
        stockinfo = Inet.get_stockinfo(self.Code)
        if not isinstance(stockinfo, dict):
            # 如果不是字典，说明访问出错
            if stockinfo == 'Time out' or stockinfo == 'Network error':
                # 超时, 网络错误
                self.window.draw_error(stockinfo)
            elif stockinfo == 'TryAll error':
                # 股票代码错误，或secid错误
                messagebox.showerror(
                    title='错误',
                    message=f'{self.Code} 股票代码错误，或secid错误！'
                )
            else:
                messagebox.showerror(
                    title='错误',
                    message='未知错误！'
                )
            return None
        # 获取个股数据成功
        print(f'个股最新数据：{stockinfo}')
        # 遍历设置动态变量
        for key, value in stockinfo.items():
            self.StringVar_DetailedInfo[key] = tk.StringVar()
            self.StringVar_DetailedInfo[key].set(value)

        # 数据处理成功，内容框架。绘制内容
        self.draw_content()

    def draw_title(self):
        """
        绘制页面标题

        :return:
        """
        # 字体框架
        # --master self.CNTL_Stock['title'] 标题框架
        frame_text_attrs = FRAME_ATTRS.copy()
        # frame_text_attrs['bg'] = 'blue'
        frame_text_place = {
            'width': SIZE_STOCKFACE_TITLE_W * 0.5,
            'height': SIZE_STOCKFACE_TITLE_H - SIZE_TBLINE_WIDTH,
        }
        frame_text = tk.Frame(
            self.CNTL_Stock['title'],
            frame_text_attrs,
        )
        frame_text.place(frame_text_place)
        # 详情
        # --master frame_text 字体框架
        label_xq_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_TIPS_TALL,
            'font': FONT_TBNAME,
            'text': '详 情'
        }
        label_xq = tk.Label(
            frame_text,
            label_xq_attrs
        )
        label_xq.pack(side='left')
        # 数据提供
        # --master frame_text 字体框架
        label_provider_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_TIPS_LOW,
            'font': FONT_WARNING,
            'text': '（以下数据由东方财富提供）'
        }
        label_provider = tk.Label(
            frame_text,
            label_provider_attrs
        )
        label_provider.pack(side='left')

        # 按钮框架
        # --master self.CNTL_Stock['title'] 标题框架
        frame_btn_attrs = FRAME_ATTRS.copy()
        # frame_btn_attrs['bg'] = 'green'
        frame_btn_place = {
            'width': SIZE_STOCKFACE_TITLE_W * 0.5,
            'height': SIZE_STOCKFACE_TITLE_H - SIZE_TBLINE_WIDTH,
            'x': frame_text_place['width']
        }
        frame_btn = tk.Frame(
            self.CNTL_Stock['title'],
            frame_btn_attrs,
        )
        frame_btn.place(frame_btn_place)
        # 返回按钮
        # --master frame_btn 按钮框架
        button_back_attrs = BUTTON_ATTRS.copy()
        button_back_attrs['text'] = '返回'
        button_back_attrs['command'] = self.event_back
        button_back = tk.Button(
            frame_btn,
            button_back_attrs
        )
        button_back.pack(side='right')
        # 加持有按钮
        # --master frame_btn 按钮框架
        button_back_attrs = BUTTON_ATTRS.copy()
        # 判断是否已经持有
        if self.Code in Ldb.read_holdlist():
            # 已持有
            button_back_attrs['text'] = '已持有'
            button_back_attrs['state'] = 'disable'  # 禁用按钮
        else:
            button_back_attrs['text'] = '加持有'

        button_back = tk.Button(
            frame_btn,
            button_back_attrs
        )
        button_back.pack(side='right', padx=10)
        # 绑定事件
        button_back.configure(
            command=lambda
            code=self.Code:
            self.event_business(code)
        )

    def draw_content(self):
        """
        绘制主体内容框架

        :return:
        """
        # 左边内容
        # --master self.CNTL_Stock 内容框架
        frame_leftcontent_attrs = FRAME_ATTRS.copy()
        # frame_leftcontent_attrs['bg'] = 'red'
        frame_leftcontent_place = {
            'width': SIZE_STOCKTREND_PIC_W,
            'height': SIZE_STOCKFACE_CONTENT_H
        }
        frame_leftcontent = tk.Frame(
            self.CNTL_Stock['content'],
            frame_leftcontent_attrs
        )
        frame_leftcontent.place(frame_leftcontent_place)
        # 右边内容
        # --master self.CNTL_Stock 内容框架
        frame_rightcontent_attrs = FRAME_ATTRS.copy()
        frame_rightcontent_attrs['bg'] = 'blue'
        frame_rightcontent_place = {
            'width': SIZE_STOCKFACE_CONTENT_W - SIZE_STOCKTREND_PIC_W,
            'height': SIZE_STOCKFACE_CONTENT_H,
            'x': SIZE_STOCKTREND_PIC_W
        }
        frame_rightcontent = tk.Frame(
            self.CNTL_Stock['content'],
            frame_rightcontent_attrs
        )
        frame_rightcontent.place(frame_rightcontent_place)

        # 个股详基本信息和细信息框架
        # --master frame_leftcontent 左边内容
        frame_detailed_basic_info_attrs = FRAME_ATTRS.copy()
        frame_detailed_basic_info_place = {
            'width': frame_leftcontent_place['width'],
            'height': frame_leftcontent_place['height'] - SIZE_STOCKTREND_PIC_H,
        }
        frame_detailed_basic_info = tk.Frame(
            frame_leftcontent,
            frame_detailed_basic_info_attrs
        )
        frame_detailed_basic_info.place(frame_detailed_basic_info_place)
        # --【基本信息框架】--
        # --master frame_detailedinfo 个股详基本信息和细信息框架
        frame_basicinfo_attrs = FRAME_ATTRS.copy()
        # frame_basicinfo_attrs['bg'] = 'red'
        frame_basicinfo_place = {
            'width': frame_detailed_basic_info_place['width'],
            'height': frame_detailed_basic_info_place['height'] * 0.3
        }
        frame_basicinfo = tk.Frame(
            frame_detailed_basic_info,
            frame_basicinfo_attrs
        )
        frame_basicinfo.place(frame_basicinfo_place)
        # --【详细信息框架】--
        # --master frame_detailedinfo 个股详基本信息和细信息框架
        frame_detailedinfo_attrs = FRAME_ATTRS.copy()
        # frame_detailedinfo_attrs['bg'] = 'red'
        frame_detailedinfo_place = {
            'width': frame_detailed_basic_info_place['width'],
            'height': frame_detailed_basic_info_place['height'] - frame_basicinfo_place['height'],
            'y': frame_basicinfo_place['height'],
        }
        frame_detailedinfo = tk.Frame(
            frame_detailed_basic_info,
            frame_detailedinfo_attrs
        )
        frame_detailedinfo.place(frame_detailedinfo_place)

        # -*-*-基本信息（股票名称、代码、涨跌幅、涨跌额、最高、最低、今开、昨收、最新价）
        # 分为5等分
        # --master frame_basicinfo 基本信息框架
        frame_cellinfo_place = {
            'width': frame_basicinfo_place['width'] / 5,
            'height': frame_basicinfo_place['height'],
        }
        # 根据涨跌幅设置字体颜色
        if float(self.StringVar_DetailedInfo['f3'].get()) < 0:
            font_color = COLOR_FALL  # 下跌
        else:
            font_color = COLOR_RISE  # 涨
        for i in range(5):
            frame_cellinfo_place['x'] = i * frame_cellinfo_place['width']
            frame_cellinfo = tk.Frame(
                frame_basicinfo,
                frame_basicinfo_attrs
            )
            frame_cellinfo.place(frame_cellinfo_place)
            # 股票名称、股票代码
            if i == 0:
                # 股票名称
                label_gpmc_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_THEME,
                    'font': FONT_STOCKINFO_DATA1,
                    'anchor': 's',
                    'textvariable': self.StringVar_DetailedInfo['f14']
                }
                label_gpmc = tk.Label(
                    frame_cellinfo,
                    label_gpmc_attrs
                )
                label_gpmc.pack(side='top', fill='y', expand='y')
                # 股票代码
                label_gpdm_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_TIPS_TALL,
                    'font': FONT_STOCKINFO_CODE,
                    'anchor': 'n',
                    'textvariable': self.StringVar_DetailedInfo['f12']
                }
                label_gpdm = tk.Label(
                    frame_cellinfo,
                    label_gpdm_attrs
                )
                label_gpdm.pack(side='top', fill='y', expand='y')
            # 最新价
            elif i == 1:
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': font_color,
                    'font': FONT_STOCKINFO_DATA1,
                    'textvariable': self.StringVar_DetailedInfo['f2']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(expand='y', fill='both')
            # 涨跌幅、涨跌额
            elif i == 2:
                # 涨跌幅
                # 设置单位
                self.StringVar_DetailedInfo['f3'].set(self.StringVar_DetailedInfo['f3'].get() + '%')
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': font_color,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f3']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')
                # 涨跌额
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': font_color,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f4']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')
            # 今开、昨收
            elif i == 3:
                # 今开
                self.StringVar_DetailedInfo['f17'].set('今开：' + self.StringVar_DetailedInfo['f17'].get())
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_THEME,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f17']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')
                # 昨收
                self.StringVar_DetailedInfo['f18'].set('昨收：' + self.StringVar_DetailedInfo['f18'].get())
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_THEME,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f18']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')
            # 最高、最低
            elif i == 4:
                # 最高
                self.StringVar_DetailedInfo['f15'].set('最高：' + self.StringVar_DetailedInfo['f15'].get())
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_THEME,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f15']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')
                # 最低
                self.StringVar_DetailedInfo['f16'].set('最低：' + self.StringVar_DetailedInfo['f16'].get())
                label_attrs = {
                    'bg': COLOR_BACKGROUND,
                    'fg': COLOR_THEME,
                    'font': FONT_STOCKINFO_DATA2,
                    'anchor': 'w',  # 文字显示在左边
                    'textvariable': self.StringVar_DetailedInfo['f16']
                }
                label = tk.Label(
                    frame_cellinfo,
                    label_attrs
                )
                label.pack(side='top', fill='x', expand='y')

        # -*-*-详细信息（）
        # 处理字段
        self.StringVar_DetailedInfo['f20'].set('总市值(元)：'+self.StringVar_DetailedInfo['f20'].get())
        self.StringVar_DetailedInfo['f21'].set('流通市值：' + self.StringVar_DetailedInfo['f21'].get())
        self.StringVar_DetailedInfo['f23'].set('市净：' + self.StringVar_DetailedInfo['f23'].get())
        self.StringVar_DetailedInfo['f9'].set('动态市盈率：' + self.StringVar_DetailedInfo['f9'].get())
        self.StringVar_DetailedInfo['f5'].set('成交量(手)：' + self.StringVar_DetailedInfo['f5'].get())
        self.StringVar_DetailedInfo['f6'].set('成交额(元)：' + self.StringVar_DetailedInfo['f6'].get())
        self.StringVar_DetailedInfo['f8'].set('换手率：' + self.StringVar_DetailedInfo['f8'].get())
        self.StringVar_DetailedInfo['f10'].set('量比：' + self.StringVar_DetailedInfo['f10'].get())
        tempLsit = [
            [
                self.StringVar_DetailedInfo['f20'],
                self.StringVar_DetailedInfo['f21'],
                self.StringVar_DetailedInfo['f23'],
                self.StringVar_DetailedInfo['f9'],
            ],
            [
                self.StringVar_DetailedInfo['f5'],
                self.StringVar_DetailedInfo['f6'],
                self.StringVar_DetailedInfo['f8'],
                self.StringVar_DetailedInfo['f10'],
            ],
        ]
        # --master frame_detailedinfo 详细信息框架
        # 框架属性
        frame_detailed2_attrs = FRAME_ATTRS.copy()
        # frame_detailed2_attrs['bg'] = 'pink'
        frame_detailed2_place = {
            'width': frame_detailedinfo_place['width'] / 2,
            'height': frame_detailedinfo_place['height'],
        }
        # 标签属性
        label_cell_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_THEME,
            'font': FONT_STOCKINFO_DATA2,
            # 'anchor': 'w',  # 文字显示在左边
        }
        for i in range(2):
            # 绘制框架
            frame_detailed2 = tk.Frame(
                frame_detailedinfo,
                frame_detailed2_attrs
            )
            frame_detailed2_place['x'] = frame_detailed2_place['width'] * i
            frame_detailed2.place(frame_detailed2_place)
            for j in range(4):
                label_cell_attrs['textvariable'] = tempLsit[i][j]
                label_cell = tk.Label(
                    frame_detailed2,
                    label_cell_attrs
                )
                label_cell.pack(side='top', fill='y', expand='y')
        # ------------------------------------------------------------------------------
        # 个股日K图片
        # --master frame_leftcontent 左边内容
        label_trendpic_attrs = {
            'bg': COLOR_BACKGROUND
        }
        label_trendpic_place = {
            'width': SIZE_STOCKTREND_PIC_W,
            'height': SIZE_STOCKTREND_PIC_H,
            'y': frame_detailed_basic_info_place['height']
        }
        # 下载图片
        if Inet.download_stocktrend(self.Code):
            self.window.tkImage['tempStockTrend'] = tk.PhotoImage(file='Cache/tempStockTrend.png')
            # 删除图片
            os.remove('Cache/tempStockTrend.png')
            # 图片下载成功
            label_trendpic_attrs['image'] = self.window.tkImage['tempStockTrend']
        else:
            # 图片下载失败
            label_trendpic_attrs['text'] = '暂无数据！'
            label_trendpic_attrs['font'] = FONT_NONEDATA_ABNORMAL
            label_trendpic_attrs['fg'] = COLOR_TIPS_LOW
        # 绘制
        label_trendpic = tk.Label(
            frame_leftcontent,
            label_trendpic_attrs
        )
        label_trendpic.place(label_trendpic_place)

        # =================================================================================
        # 统计框架
        # --master frame_rightcontent 右边内容框架
        frame_total_attrs = FRAME_ATTRS.copy()
        frame_total_place = {
            'width': frame_rightcontent_place['width'],
            'height': frame_rightcontent_place['height'] * 0.15,
        }
        frame_total = tk.Frame(
            frame_rightcontent,
            frame_total_attrs
        )
        frame_total.place(frame_total_place)
        # 历史总收益框架
        # --master frame_total 统计框架
        frame_profit_attrs = FRAME_ATTRS.copy()
        # frame_profit_attrs['bg'] = 'pink'
        frame_profit_place = {
            'width': frame_total_place['width'] / 2,
            'height': frame_total_place['height'],
            'x': 0,
        }
        total_tips = ['近15条历史总收益', '近15条历史总收益率']
        stringvar_profitTotal = [tk.StringVar(), tk.StringVar()]
        cntl_total = []
        # stringvar_profitTotal[0]总收益动态变量
        # stringvar_profitTotal[1]总收益率动态变量
        for i in range(2):
            frame_profit = tk.Label(
                frame_total,
                frame_profit_attrs
            )
            frame_profit_place['x'] += frame_profit_place['width'] * i
            frame_profit.place(frame_profit_place)
            # 统计标题
            # --master frame_profit 历史总收益tips
            label_totalprofit_attrs = {
                'fg': COLOR_TIPS_LOW,
                'bg': COLOR_BACKGROUND,
                'font': FONT_OPTIONRECORD_TOTAL,
                'text': total_tips[i],
                'anchor': 's'
            }
            label_totalprofit = tk.Label(
                frame_profit,
                label_totalprofit_attrs
            )
            label_totalprofit.pack(side='top', fill='y', expand='y')
            # 统计数据
            # --master frame_profit 历史总收益tips
            label_totaldata_attrs = {
                'fg': COLOR_TIPS_LOW,
                'bg': COLOR_BACKGROUND,
                'font': FONT_OPTIONRECORD_TOTAL,
                'textvariable': stringvar_profitTotal[i],
                'anchor': 'n'
            }
            label_totaldata = tk.Label(
                frame_profit,
                label_totaldata_attrs
            )
            label_totaldata.pack(side='top', fill='y', expand='y')
            cntl_total.append(label_totaldata)
        # ----------------------------------------------------
        # 列表框架
        # --master frame_rightcontent 右边内容框架
        frame_list_attrs = FRAME_ATTRS.copy()
        # frame_list_attrs['bg'] = 'red'
        frame_list_place = {
            'width': frame_rightcontent_place['width'],
            'height': frame_rightcontent_place['height'] - frame_total_place['height'],
            'y': frame_total_place['height'],
        }
        frame_list = tk.Frame(
            frame_rightcontent,
            frame_list_attrs
        )
        frame_list.place(frame_list_place)
        # -----------
        # 表格标题
        # --master frame_list 列表框架
        label_tname_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_TIPS_LOW,
            'font': FONT_OPTIONRECORD_TNAME,
            'text': '操作记录（仅展示最近15条记录！）',
            'anchor': 'w'
        }
        label_tname_place = {
            'width': frame_list_place['width'],
            'height': frame_list_place['height'] * 0.05,
        }
        label_tname = tk.Label(
            frame_list,
            label_tname_attrs
        )
        label_tname.place(label_tname_place)
        # 表格表头框架
        # --master frame_list 列表框架
        frame_thead_attrs = CANVAS_ATTRS.copy()
        frame_thead_place = {
            'width': frame_list_place['width'],
            'height': frame_list_place['height'] * 0.06,
            'y': label_tname_place['height'],
        }
        frame_thead = tk.Canvas(
            frame_list,
            frame_thead_attrs
        )
        frame_thead.place(frame_thead_place)
        # 绘制表头表格线条
        draw_tb_line(frame_thead, frame_thead_place, tt=(1, 1))
        # 表身框架
        # --master frame_list 列表框架
        frame_tbody_attrs = FRAME_ATTRS.copy()
        # frame_tbody_attrs['bg'] = 'pink'
        frame_tbody_place = {
            'width': frame_list_place['width'],
            'height': frame_list_place['height'] - frame_thead_place['height'],
            'y': frame_thead_place['height'] + frame_thead_place['y'],
        }
        frame_tbody = tk.Frame(
            frame_list,
            frame_tbody_attrs
        )
        frame_tbody.place(frame_tbody_place)

        # -----------------------------
        # 表头字段
        # --master frame_thead 表头框架
        thead_text = ('持有时间段', '天数', '收益(元)', '收益率')
        thead_width = (0.40, 0.10, 0.30, 0.20)
        label_thead_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_TIPS_LOW,
            'font': FONT_OPTIONRECORD_THEAD,
        }
        label_thead_place = {
            'width': 0,
            'height': frame_thead_place['height'] - SIZE_TBLINE_WIDTH * 2,
            'x': 0,
            'y': SIZE_TBLINE_WIDTH
        }
        # 遍历绘制表头字段
        for index in range(len(thead_text)):
            label_thead_attrs['text'] = thead_text[index]
            label_thead = tk.Label(
                frame_thead,
                label_thead_attrs
            )
            label_thead_place['x'] += label_thead_place['width']
            label_thead_place['width'] = frame_thead_place['width'] * thead_width[index]
            label_thead.place(label_thead_place)

        # -*-*-判断是否有数据
        historyOption_list = Ldb.read_optionhistory(self.Code)
        if not isinstance(historyOption_list, tuple):
            # 无数据
            self.window.draw_errors(frame_tbody, 'None data')
        else:
            # 有记录
            print(f'读取的操作记录列表：{historyOption_list}')
            # 设置统计数据，根据值给出不同颜色
            if historyOption_list[1][0] > 0:
                cntl_total[0]['fg'] = COLOR_RISE
                cntl_total[1]['fg'] = COLOR_RISE
            else:
                cntl_total[0]['fg'] = COLOR_FALL
                cntl_total[1]['fg'] = COLOR_FALL
            stringvar_profitTotal[0].set(historyOption_list[1][0])
            stringvar_profitTotal[1].set(historyOption_list[1][1])
            # 按行绘制数据
            for aline in historyOption_list[0]:
                # 一行框架
                # --master frame_tbody 表身框架
                frame_aline_attrs = CANVAS_ATTRS.copy()
                frame_aline_attrs['width'] = frame_tbody_place['width']
                frame_aline_attrs['height'] = SIZE_STOCKINFO_ALINE_H
                frame_aline = tk.Canvas(
                    frame_tbody,
                    frame_aline_attrs
                )
                frame_aline.pack()
                draw_tb_line(frame_aline, frame_aline_attrs, tt=(0, 1))
                # 单元格数据
                # --master frame_aline 一行框架
                label_data_attrs = {
                    'bg': COLOR_BACKGROUND
                }
                label_data_place = {
                    'width': 0,
                    'height': frame_aline_attrs['height']  - SIZE_TBLINE_WIDTH,
                    'x': 0
                }
                for cell_index in range(len(thead_width)):
                    # print(aline[cell_index])
                    # 根据收益设置字体颜色
                    if aline[2] > 0:
                        fg = COLOR_RISE
                    elif aline[2] < 0:
                        fg = COLOR_FALL
                    else:
                        fg = COLOR_TIPS_TALL
                    label_data_attrs['fg'] = fg
                    label_data_attrs['text'] = aline[cell_index]
                    label_data = tk.Label(
                        frame_aline,
                        label_data_attrs
                    )
                    label_data_place['x'] += label_data_place['width']
                    label_data_place['width'] = frame_aline_attrs['width'] * thead_width[cell_index]
                    label_data.place(label_data_place)

    def event_back(self):
        """
        返回事件

        :return:
        """
        if self.window.tdResult_Choice:
            # 返回荐股结果页面
            ChoiceResult(self.window, self.SortColumn, self.SortMode, self.NowPageNum)
        else:
            ChoiceFace(self.window)


class Tabel(FACE):
    """表格形式的布局类，即所有表格形式的页面继承该类"""
    def __init__(self, window):
        FACE.__init__(self, window)

        self.CNTL_Tabel = {}  # 存储表格基础框架控件对象
        # key:
        #     'name'  表格名称框架
        #     'thead'  表头框架
        #     'tbody'  表身框架
        #     'page'  表格分页框架

        # -----TName表格统计相关变量 ↓↓↓↓↓↓↓↓↓↓-----
        self.TotalText = ()  # 存储统计的字段
        self.CNTL_TotalData = None  # 存储统计数据Label控件对象，方便改变状态
        # -----TName表格标题相关变量 ↓↓↓↓↓↓↓↓↓↓-----

        # -----Thead表头相关变量 ↓↓↓↓↓↓↓↓↓↓-----
        self.TheadText = {}
        """
        Example:
            # 子类赋值
            self.TheadText = {
                '序号': {
                    'param': None,
                    'width': 0.1,  # 该列的宽度占比，总宽度不超过 1
                    'CNTL': ,  # 表头控件对象，用于改变状态
                },
                ......
            }
            # self.TheadText的keys内容就是表头的内容，数量就是数据列数
            self.draw_thead()  # 调用绘制表头，即可绘制表头
        """
        # -----Thead表头相关变量 ↑↑↑↑↑↑↑↑↑↑-----

        self.Mode = None  # 存储当前模式
        # self.Mode = 1  持有
        # self.Mode = 2  历史

        # -----Tbody表身相关变量 ↓↓↓↓↓↓↓↓↓↓-----
        self.CNTL_TabelTbody = None
        # 如果有滚动条, self.CNTL_TabelTbody 就是滚动条视图
        # 如果没有滚动条, self.CNTL_TabelTbody 就指向 self.CNTL_Tabel['tbody']。
        self.Tbody_DataLIST = None  # 存储表身数据
        self.Total_Data = None  # 存储统计数据
        self.SortColumn = None  # 当前排序列
        self.SortMode = None  # 当前排序方式
        # -----Tbody表身相关变量 ↑↑↑↑↑↑↑↑↑↑-----

        # -----分页相关变量 ↓↓↓↓↓↓↓↓↓↓-----
        self.PageNum = 10  # 每页的数据条目
        self.NowPageNum = 0  # 当前页数
        self.PageTotalNum = 0  # 总页数
        self.LENDataLIST = 0  # 总数据条目
        self.StringVar_PageInfo = tk.StringVar()  # 页数动态显示
        self.StringVar_JumpEntry = tk.StringVar()
        # -----分页相关变量 ↑↑↑↑↑↑↑↑↑↑-----

        # 绘制表格形式的布局
        self.table_layout()

    def table_layout(self):
        """
        绘制表格形式的布局

        """

        # --表格名称框架-----------------------
        # -- master: self.window.CNTL_NowFace 桌面
        frame_tabelname_attrs = FRAME_ATTRS.copy()
        # frame_tabelname_attrs['bg'] = 'red'
        frame_tabelname_palce = {
            'width': SIZE_TBNAME_W,
            'height': SIZE_TBNAME_H,
            'x': SIZE_TABEL_MARGIN,
            'y': 0,
        }
        self.CNTL_Tabel['name'] = tk.Frame(
            self.window.CNTL_NowFace,  # 基于当前的桌面
            frame_tabelname_attrs
        )
        self.CNTL_Tabel['name'].place(frame_tabelname_palce)
        # --表格名称框架-----------------------

        # --表格框架-----------------------
        # -- master: self.window.CNTL_NowFace 桌面
        frame_tabel_attrs = FRAME_ATTRS.copy()
        # frame_tabel_attrs['bg'] = 'blue'
        frame_tabel_place = {
            'width': SIZE_TB_W,
            'height': SIZE_TB_H,
            'x': SIZE_TABEL_MARGIN,
            'y': frame_tabelname_palce['y'] + frame_tabelname_palce['height']
        }
        frame_tabel = tk.Frame(
            self.window.CNTL_NowFace,  # 基于当前的桌面
            frame_tabel_attrs
        )
        frame_tabel.place(frame_tabel_place)
        # 表头框架
        # --master frame_tabel 表格框架
        canvas_thead_atrrs = CANVAS_ATTRS.copy()
        # canvas_thead_atrrs['bg'] = 'yellow'
        canvas_thead_place = {
            'width': SIZE_THEAD_W,
            'height': SIZE_THEAD_H,
        }
        self.CNTL_Tabel['thead'] = tk.Canvas(
            frame_tabel,
            canvas_thead_atrrs
        )
        self.CNTL_Tabel['thead'].place(canvas_thead_place)
        # 绘制表头线条
        draw_tb_line(self.CNTL_Tabel['thead'], canvas_thead_place)
        # 表身框架
        # --master frame_tabel 表格框架
        frame_tbody_attrs = FRAME_ATTRS.copy()
        # frame_tbody_attrs['bg'] = 'pink'
        frame_tbody_place = {
            'width': SIZE_TBODY_W,
            'height': SIZE_TBODY_H,
            'y': SIZE_THEAD_H,
        }
        self.CNTL_Tabel['tbody'] = tk.Frame(
            frame_tabel,
            frame_tbody_attrs
        )
        self.CNTL_Tabel['tbody'].place(frame_tbody_place)
        # --表格框架-----------------------

        # --分页画布-----------------------
        # -- master: self.window.CNTL_NowFace 桌面
        canvas_paging_attrs = CANVAS_ATTRS.copy()
        # canvas_paging_attrs['bg'] = 'green'
        canvas_paging_place = {
            'width': SIZE_TBPAGE_W,
            'height': SIZE_TBPAGE_H,
            'x': SIZE_TABEL_MARGIN,
            'y': frame_tabel_place['y'] + frame_tabel_place['height']
        }
        self.CNTL_Tabel['page'] = tk.Canvas(
            self.window.CNTL_NowFace,  # 基于当前的桌面
            canvas_paging_attrs
        )
        self.CNTL_Tabel['page'].place(canvas_paging_place)
        # 绘制分页分割线
        draw_tb_line(self.CNTL_Tabel['page'], canvas_paging_place, tt=(1, 0))
        # --分页画布-----------------------

    def destory_tname(self):
        """清空表格标题，方便重新绘制"""

        for widget in self.CNTL_Tabel['name'].winfo_children():
            widget.destroy()

    def destroy_tbody(self):
        """清空表身，方便重新绘制"""

        for widget in self.CNTL_Tabel['tbody'].winfo_children():
            widget.destroy()
        self.CNTL_TabelTbody = None

    def destroy_tpage(self):
        """清空分页框架，方便重新绘制"""

        for widget in self.CNTL_Tabel['page'].winfo_children():
            widget.destroy()

    def refresh(self):
        """
        刷新整个表格页面

        :return:
        """
        # 绘制表格标题
        self.draw_tname()
        # 绘制表头
        self.draw_thead()
        # 读取数据
        self.read_tbdata()
        # 判断是否有数据
        if self.Tbody_DataLIST:
            if isinstance(self.Tbody_DataLIST, str):
                # 禁用表头排序
                self.disabel_theadbtn()
                # 绘制异常
                self.window.draw_errors(self.CNTL_Tabel['tbody'], self.Tbody_DataLIST)
            else:
                # print(f'读取到了数据：{self.Tbody_DataLIST}')
                if self.TotalText:
                    # 设置统计数据
                    self.stringvar_set()
                # 绘制表身
                self.draw_tbody()
                # 绘制分页
                self.draw_tpage()
        else:
            # 禁用表头排序
            self.disabel_theadbtn()
            # 无数据
            self.window.draw_errors(self.CNTL_Tabel['tbody'], 'None data')

    def stringvar_set(self):
        """
        设置统计数据

        :return:
        """
        fg = COLOR_THEME
        # 根据收益改变字体颜色
        if self.Total_Data[2] > 0.0:
            fg = COLOR_RISE
        elif self.Total_Data[2] < 0.0:
            fg = COLOR_FALL
        # 遍历设置统计数据
        for index in range(len(self.CNTL_TotalData)):
            if index > 1:
                self.CNTL_TotalData[index]['fg'] = fg

            if index == 3:
                # 给收益率加上%
                self.CNTL_TotalData[index]['text'] = str(round(self.Total_Data[index], 2)) + '%'
            else:
                self.CNTL_TotalData[index]['text'] = round(self.Total_Data[index], 2)

    def read_tbdata(self):
        """
        读取并处理表格数据

        :return:
        """
        self.LENDataLIST = 0  # 重置总数

        self.Tbody_DataLIST, self.Total_Data = Ldb.read_localdata(self.Mode, self.SortColumn, self.SortMode)
        print(f'读取到的本地数据:{self.Tbody_DataLIST}')
        if self.Tbody_DataLIST:
            self.Tbody_DataLIST = handle_paging(self.Tbody_DataLIST, self.PageNum)
            for page in self.Tbody_DataLIST:
                self.LENDataLIST += len(page)  # 总数据条目
            self.PageTotalNum = len(self.Tbody_DataLIST)  # 总页数

    def draw_tname(self):
        """
        绘制表格标题

        :return:
        """
        self.destory_tname()

        self.CNTL_TotalData = []  # 清空存储的控件
        lenTotal = len(self.TotalText)
        # 统计框架
        # --master self.Tabel['name'] 表格标题框架
        frame_totalterm_attrs = FRAME_ATTRS.copy()
        frame_totalterm_attrs['bg'] = COLOR_BACKGROUND
        frame_totalterm_attrs['width'] = SIZE_TBNAME_W / lenTotal
        frame_totalterm_attrs['height'] = SIZE_TBNAME_H
        for index in range(lenTotal):
            # 统计框架
            frame_totalterm = tk.Frame(
                self.CNTL_Tabel['name'],
                frame_totalterm_attrs
            )
            frame_totalterm.pack(side='left', fill='x', expand='y')
            # 合计项tip
            # -- master: frame_totalterm 合计项框架
            label_totaltip_attrs = {
                'bg': COLOR_BACKGROUND,
                'fg': COLOR_TIPS_LOW,
                'font': FONT_TBTOTAL,
                'text': self.TotalText[index],
            }
            label_totaltip = tk.Label(
                frame_totalterm,
                label_totaltip_attrs
            )
            label_totaltip.pack(side='top', fill='both', expand='y')

            # 合计数据
            # -- master: frame_totalterm 合计项框架
            label_total_attrs = {
                'bg': COLOR_BACKGROUND,
                'fg': COLOR_THEME,
                'font': FONT_TBTOTAL2,
            }
            # 位置处在合计的下方
            label_total = tk.Label(
                frame_totalterm,
                label_total_attrs
            )
            label_total.pack(side='top', fill='x', expand='y')
            self.CNTL_TotalData.append(label_total)

    def draw_thead(self):
        """
        Example:
            # 子类赋值
            self.TheadText = {
                '序号': {
                    'param': None,
                    'width': 0.1,  # 该列的宽度占比，总宽度不超过 1
                },
                ......
            }
            # self.TheadText的keys内容就是表头的内容，数量就是数据列数
            self.draw_thead()  # 调用绘制表头，即可绘制表头

        :return:
        """
        # print(f'需要绘制的表头信息：{self.TheadText}')
        # 判断总占比是否超过 1
        percentage = 0
        for t in self.TheadText.values():
            percentage += t['width']
        if percentage > 1.000000:
            print(f'\n\n框架占比错误：{percentage}')
            exit()  # 退出程序

        # --通用属性
        all_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_THEAD_FONT,
            'font': FONT_THEAD,
        }
        # --无排序操作的表头基本信息，即Label
        label_thead_attrs = all_attrs.copy()

        # --有排序操作的表头，即Button
        button_thead_attrs = all_attrs.copy()
        button_thead_attrs['activebackground'] = COLOR_BACKGROUND
        button_thead_attrs['activeforeground'] = COLOR_THEAD_FONT
        button_thead_attrs['bd'] = 0
        button_thead_attrs['cursor'] = 'mouse'  # 鼠标移动到控件的鼠标状态
        button_thead_attrs['disabledforeground'] = COLOR_THEAD_FONT,  # 禁用时的按钮文本颜色

        # --表头的place布局
        all_thead_place = {
            'height': SIZE_THEAD_H - (SIZE_TBLINE_WIDTH * 2),  # 防止挡住表格线条
            'y': SIZE_TBLINE_WIDTH,
        }

        # 设置上一个表头布局
        last_thead_place = all_thead_place.copy()
        last_thead_place['x'] = 0
        last_thead_place['width'] = 0
        # 遍历绘制表头
        for key in self.TheadText.keys():
            # print(f'正在绘制“{key}”表头')

            # 判断有无排序功能, 绘制不同类型的不同
            # --master self.CNTL_Tabel['thead'] 表头框架
            # label_thead_attrs['bg'] = test[randint(0, 3)]
            sort_ = False
            if not self.TheadText[key]['param']:
                # 如果有参数，说明该按钮可排序
                # print('不可排序！')
                thead = tk.Label(
                    self.CNTL_Tabel['thead'],
                    label_thead_attrs
                )
            else:
                # print('可排序！')
                # 判断当前绘制的是否排序表头
                if self.SortColumn in self.TheadText[key]['param']:
                    # print(f'当前排序的列：{key}')
                    # 得到当前排序字段的下标，后面需要根据下标设置 排序标识
                    colindex = self.TheadText[key]['param'].index(self.SortColumn)
                    sort_ = True
                else:
                    sort_ = False
                thead = tk.Button(
                    self.CNTL_Tabel['thead'],
                    button_thead_attrs
                )
                # 绑定排序事件
                thead.configure(
                    command=lambda
                    param=self.TheadText[key]['param']:  # 排序需要的参数
                    self.event_sort(param)
                )
                # 存储控件对象
                self.TheadText[key]['CNTL'] = thead

            # 根据当前排序状态, 重新设置表头信息
            if sort_:
                # 设置当前排序列的背景
                thead['bg'] = COLOR_TBSORT_BG
                thead['fg'] = COLOR_TIPS_TALL
                # 设置当前排序标识
                texts = key.split('/')
                if self.SortMode == 'desc':
                    texts[colindex] = texts[colindex] + ' ↓'
                elif self.SortMode == 'asc':
                    texts[colindex] = texts[colindex] + ' ↑'
                # 设置排序标识
                theadtext = ''
                for ttext in texts:
                    theadtext += ttext + '/'
                theadtext = theadtext[:-1]  # 去掉最后的 /
                thead['text'] = theadtext
            else:
                thead['text'] = key

            # 设置表头布局
            thead_place = all_thead_place.copy()
            thead_place['x'] = last_thead_place['x'] + last_thead_place['width']
            thead_place['width'] = SIZE_THEAD_W * self.TheadText[key]['width']
            # 记录上一个表头布局
            last_thead_place = thead_place.copy()
            # 打包控件
            thead.place(thead_place)

    def draw_tbody_scroll(self):
        """
        判断是否需要滚动条

        :return:
        """
        # 清空表身
        self.destroy_tbody()

        # !*!*!*-- 判断是否需要滚动条 --!*!*!*
        # 如果本页数据的总高度 > 表身的高度。 说明需要滚动条
        # SIZE_TLINE_H 一行的高度
        # SIZE_TBODY_H 表身的高度
        if len(self.Tbody_DataLIST[self.NowPageNum]) * SIZE_TLINE_H > SIZE_TBODY_H:
            # print('需要滚动条')
            mysf = MyScrollFrame(self.CNTL_Tabel['tbody'])  # 滚动条类
            mysf.pack(side="top", fill="both", expand=True)  # 打包滚动条
            self.CNTL_TabelTbody = mysf.get_container()  # 返回可滚动视图，即表身数据容器
        else:
            self.CNTL_TabelTbody = self.CNTL_Tabel['tbody']
        # !*!*!*-- 判断是否需要滚动条 --!*!*!*

    def draw_tbody(self):
        """
        绘制表身

        :return:
        """

        # 判断是否需要滚动条
        self.draw_tbody_scroll()

        pageData = self.Tbody_DataLIST[self.NowPageNum]
        print(f'第 {self.NowPageNum} 页数据：{pageData}')
        # 获取持有列表
        holdList = Ldb.read_holdlist()
        # 遍历每行数据
        for index in range(len(pageData)):
            self.draw_aline(index, holdList)

    def draw_frame_aline(self, place=None):
        """
        根据表头，绘制一行和数据单元格框架

        :param place: 是否开启palce布局方式, 传参palce需要的参数{}
        :return:
            【dict】 - {
                '序号': <tkinter.Frame object .!frame2.!frame2.!frame.!canvas.!frame>,  # 对应的控件对象
                ......
            }
        """
        # 返回字典
        rtrn = {}
        # 判断表身框架
        if not self.CNTL_TabelTbody:
            cntl_ = self.CNTL_Tabel['tbody']
        else:
            cntl_ = self.CNTL_TabelTbody
        # 一行画布
        # --master self.CNTL_TabelTbody 表身框架
        canvas_aline_attrs = CANVAS_ATTRS.copy()
        # canvas_aline_attrs['bg'] = 'red'
        canvas_aline_attrs['width'] = SIZE_TLINE_W
        canvas_aline_attrs['height'] = SIZE_TLINE_H
        aline = tk.Canvas(
            cntl_,
            canvas_aline_attrs
        )
        # 判断布局方式
        if not place:
            aline.pack()
        else:
            aline.place(place)
        # 绘制表行的线条
        draw_tb_line(aline, canvas_aline_attrs, tt=(0, 1))

        # 单元格
        # --master aline 一行画布
        frame_cell_attrs = FRAME_ATTRS.copy()
        # frame_cell_attrs['bg'] = 'blue'
        frame_cell_place = {
            'height': SIZE_TLINE_H - SIZE_TBLINE_WIDTH
        }
        # 初始位置
        last_place = frame_cell_place.copy()
        last_place['x'] = 0
        last_place['width'] = 0
        # 根据表头，遍历绘制单元格框架
        for k in self.TheadText.keys():
            # print(f'宽度{self.TheadText[k]["width"]}')
            frame_cell_place['x'] = last_place['x'] + last_place['width']
            frame_cell_place['width'] = self.TheadText[k]["width"] * SIZE_TLINE_W

            cell = tk.Frame(
                aline,
                frame_cell_attrs
            )
            cell.place(frame_cell_place)
            # 记录上一个控件位置
            last_place = frame_cell_place.copy()
            # 存储控件对象
            rtrn[k] = {'CNTL': cell}
            rtrn[k]['place'] = frame_cell_place.copy()

        return rtrn

    def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
        """
        一行一行绘制数据，字类继承重写

        :param rowsindex: 行数下标
        :param holdlist: 持有列表
        :param btn3state: 展开按钮状态
        :param place: 是否开启place布局
        :return:
        """

    def draw_tpage(self):
        """
        绘制分页

        :return:
        """
        frame_paging_attrs = FRAME_ATTRS.copy()
        # frame_paging_attrs['bg'] = 'red'
        frame_paging_place = {
            'width': SIZE_TBPAGE_W,
            'height': SIZE_TBPAGE_H - SIZE_TBLINE_WIDTH,
            'x': 0,
            'y': SIZE_TBLINE_WIDTH,
        }
        frame_paging = tk.Frame(
            self.CNTL_Tabel['page'],
            frame_paging_attrs
        )
        frame_paging.place(frame_paging_place)
        # 页数和数据条目
        # -- master: frame_paging 页数信息框架
        label_pageinfo_attrs = LABEL_PAGE_ATTRS.copy()
        # label_pageinfo_attrs['bg'] = 'blue'
        label_pageinfo_attrs['textvariable'] = self.StringVar_PageInfo
        label_pageinfo = tk.Label(
            frame_paging,
            label_pageinfo_attrs
        )
        label_pageinfo.pack(side='right', fill='y')
        # 设置页数信息
        if self.PageTotalNum > 1:
            # 页数大于1时才绘制按钮
            self.StringVar_PageInfo.set(
                f'第 {self.NowPageNum + 1} 页，共 {self.PageTotalNum} 页，每页 {self.PageNum} 条，共 {self.LENDataLIST} 条'
            )
            # 判断翻页按钮属性
            # ========判断分页按钮状态======
            # 'normal' : 启用
            # 'disabled' : 禁用
            next_state = 'normal'  # 下一页
            previous_state = 'normal'  # 上一页
            first_state = 'normal'  # 首页
            last_state = 'normal'  # 末页

            if self.NowPageNum == 0:  # 在首页
                first_state = 'disabled'
                previous_state = 'disabled'
            elif self.NowPageNum + 1 == self.PageTotalNum:  # 如果当前页 + 1 == 页数，说明在尾页
                next_state = 'disabled'
                last_state = 'disabled'
            # **按钮属性**
            button_page_attrs = {
                'fg': COLOR_BTNFG,
                'bg': COLOR_BACKGROUND,
                'font': FONT_TBPAGE,
                "relief": "solid",
                "bd": 1,
                "activebackground": COLOR_BACKGROUND,
                "activeforeground": COLOR_BTNFG,
            }

            # 末页
            # -- master frame_paging 分页框架
            button_lastpage_attrs = button_page_attrs.copy()
            button_lastpage_attrs['text'] = ' 末页 '
            button_lastpage_attrs['state'] = last_state
            button_lastpage = tk.Button(
                frame_paging,
                button_lastpage_attrs
            )
            button_lastpage.pack(side='right', padx=5)
            # self.CNTL_Page.append(button_lastpage)
            button_lastpage.configure(command=lambda: self.turn_page('last'))

            # 下一页按钮
            # -- master frame_paging 分页框架
            button_nextpage_attrs = button_page_attrs.copy()
            button_nextpage_attrs['text'] = ' 下一页 '
            button_nextpage_attrs['state'] = next_state
            button_nextpage = tk.Button(
                frame_paging,
                button_nextpage_attrs
            )
            button_nextpage.pack(side='right', padx=5)
            # self.CNTL_Page.append(button_nextpage)
            button_nextpage.configure(command=lambda: self.turn_page('next'))

            # 上一页按钮
            # -- master frame_paging 分页框架
            button_previouspage_attrs = button_page_attrs.copy()
            button_previouspage_attrs['text'] = ' 上一页 '
            button_previouspage_attrs['state'] = previous_state
            button_previouspage = tk.Button(
                frame_paging,
                button_previouspage_attrs
            )
            button_previouspage.pack(side='right', padx=5)
            # self.CNTL_Page.append(button_previouspage)
            button_previouspage.configure(command=lambda: self.turn_page('previous'))

            # 首页按钮
            # -- master self.CNTL_Paging 分页框架
            button_firstpage_attrs = button_page_attrs.copy()
            button_firstpage_attrs['text'] = ' 首页 '
            button_firstpage_attrs['state'] = first_state
            button_firstpage = tk.Button(
                frame_paging,
                button_firstpage_attrs
            )
            button_firstpage.pack(side='right', padx=5)
            # self.CNTL_Page.append(button_firstpage)
            button_firstpage.configure(command=lambda: self.turn_page('first'))

            # 页 Label
            # -- master self.CNTL_Paging 分页框架
            # 属性在前面
            label_ye = tk.Label(
                frame_paging,
                LABEL_PAGE_ATTRS.copy()
            )
            label_ye.pack(side='right', fill='y')

            # 输入框架
            # -- master self.CNTL_Paging 分页框架
            # entry_line_attrs = FRAME_ATTRS.copy()
            frame_entry_attrs = {
                'bg': COLOR_BACKGROUND,
                'width': 55,
                'height': 30,
            }
            frame_entry = tk.Frame(
                frame_paging,
                frame_entry_attrs
            )
            frame_entry.pack(side='right', padx=5)
            # 页数输入框
            # -- master frame_entry 输入框架
            # 设置跳转页显示
            self.StringVar_JumpEntry.set("")
            entry_page_attrs = {
                'bg': COLOR_BACKGROUND,
                'bd': 0,
                'font': FONT_TBPAGE,
                'fg': COLOR_TIPS_TALL,
                # 'relief': 'solid',
                'justify': 'center',
                # 'state': entry_state,
                'textvariable': self.StringVar_JumpEntry,
                'validate': "key",
                'validatecommand': (self.window.OnlyNumber, '%P'),  # 显示entry只能输入数字
                # 'width': 5,
            }
            entry_page_place = {
                'width': frame_entry_attrs['width'],
                'height': frame_entry_attrs['height'] - 2,
                'x': 0,
                'y': 0
            }
            entry_page = tk.Entry(
                frame_entry,
                entry_page_attrs
            )
            entry_page.place(entry_page_place)
            # 输入框横线
            # --master frame_entry 输入框架
            entryline_attrs = {
                'bg': 'black'
            }
            entryline_place = {
                'width': frame_entry_attrs['width'],
                'height': 2,
                'y': entry_page_place['height']
            }
            entryline = tk.Frame(
                frame_entry,
                entryline_attrs
            )
            entryline.place(entryline_place)

            # 跳转按钮
            # -- master self.CNTL_Paging 分页框架
            button_jumppage_attrs = button_page_attrs.copy()
            button_jumppage_attrs['text'] = ' 跳转至 '
            button_jumppage = tk.Button(
                frame_paging,
                button_jumppage_attrs
            )
            button_jumppage.pack(side='right', padx=5)
            button_jumppage.configure(command=lambda: self.jump_page())

        else:
            self.StringVar_PageInfo.set(f'合计 {self.LENDataLIST} 条')

    def event_sort(self, params: list):
        """
        排序事件

        :param params: 参数列表 - 0 排序的列key； 1 排序的字段； 2 排序的方式
        :return:
        """
        # print(f'参数：{params}')
        # print(f'当前排序状态：{self.SortColumn, self.SortMode}')

        # 判断当前点击的列是否当前排序的列
        if self.SortColumn in params:
            # print('点击了当前正在排序的列')
            if self.SortMode == 'asc':
                # 如果当前排序方式是'asc'，需要更换字段
                self.SortMode = 'desc'  # 设置排序方式
                if self.SortColumn == params[-1]:
                    # 如果当前排序字段是最后一个，直接跳转第一个
                    self.SortColumn = params[0]
                else:
                    # 如果不是最后一个，跳转下一个
                    i = params.index(self.SortColumn) + 1
                    self.SortColumn = params[i]
            elif self.SortMode == 'desc':
                # 如果是'desc'，直接改变排序方式即可
                self.SortMode = 'asc'
        else:
            # print('点击了非当前正在排序的列')
            self.SortColumn = params[0]
            self.SortMode = 'desc'
        # print(f'更改后的排序状态：{self.SortColumn, self.SortMode}')

        # 重新绘制页面
        self.refresh()

    def turn_page(self, operation):
        """
        翻页事件

        :param operation: list [按钮对象， 操作]
                'last' : 末页
                'next' : 下一页
                'previous' : 上一页
                'first' : 首页
        :return:
        """
        if operation == 'last':
            self.NowPageNum = self.PageTotalNum - 1
        elif operation == 'next':
            self.NowPageNum += 1
        elif operation == 'previous':
            self.NowPageNum -= 1
        elif operation == 'first':
            self.NowPageNum = 0

        # 翻页
        self.draw_tbody()
        # 重绘分页
        self.draw_tpage()

    def jump_page(self):
        """
        跳转指定页

        :return:
        """
        # print(f'输入的值：{self.StringVar_JumpEntry.get()}')
        try:
            input_page = int(self.StringVar_JumpEntry.get())  # 得到当前输入的页
        except ValueError:
            # 输入为空，不做任何操作
            return None

        if input_page >= self.PageTotalNum:  # 如果输入的页数 > 总页数 直接翻页到最后
            jump = self.PageTotalNum - 1
        elif input_page <= 1:  # 如果输入的页数 <= 1 直接翻页到首页
            jump = 0
        else:  # 其他页面直接翻到指定页
            jump = input_page

        # 如果输入的与当前一致，不做任何操作
        if jump == self.NowPageNum:
            return None
        self.NowPageNum = jump  # 改变当前页数

        self.draw_tbody()
        # 重绘分页
        self.draw_tpage()

    def disabel_theadbtn(self):
        """禁用表头排序"""

        for value in self.TheadText.values():
            if value['param']:
                # 只有有排序功能的按钮才能禁用
                value['CNTL']['state'] = 'disable'  # 禁用按钮


class HoldFace(Tabel):
    def __init__(self, window):
        Tabel.__init__(self, window)
        # --No.1--初始化排序----------
        self.Mode = 1  # 持有
        self.SortColumn = 'UpAndDownRange'
        self.SortMode = 'desc'

        # --No.2--初始化统计----------
        self.TotalText = ('持有总成本', '持有总市值', '持有总收益', '持有总收益率')

        # --No.3--初始化表头----------
        self.TheadText = {
            '名称/代码': {'param': ('Name', 'Code'), 'width': 0.18},
            '加入价格/时间': {'param': ('BuyPrice', 'BuyDate'), 'width': 0.22},
            '最新价格/时间': {'param': ('Price', 'Date'), 'width': 0.22},
            '涨跌': {'param': ('UpAndDownPrice',), 'width': 0.12},
            '涨跌幅': {'param': ('UpAndDownRange',), 'width': 0.12},
            '操作': {'param': None, 'width': 0.14},
        }  # param不会空表示，该表头可排序。内容即，sort需要的参数。使用关键字参数

        # 存储定时更新最新结果的线程
        self.tdResult = None

        # 绘制页面
        self.refresh()
        # 开启定时刷新持有
        if not self.window.monitorState_Hold:
            print('开启监听！')
            self.window.monitorState_Hold = True
            self.monitor_hold_tdresult()

    def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
        """
        重写父类，方法。绘制一行数据

        :param rowsindex:
        :param holdlist:
        :param btn3state:
        :param place:
        :return:
        """
        aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
        # 通用属性
        attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_THEME,
            'font': FONT_TBDATA1,
        }
        # 绘制行框架
        cntl_dict = self.draw_frame_aline(place=place)
        # 遍历绘制单元格数据
        for key, cntl in cntl_dict.items():
            if key in ('涨跌', '涨跌幅'):
                # 根据涨跌设置字体颜色
                fg = COLOR_THEME
                if aline[7] > 0:
                    fg = COLOR_RISE
                elif aline[7] < 0:
                    fg = COLOR_FALL
                # 判断是哪一列
                if key == '涨跌':
                    text = round(aline[7], 2)
                elif key == '涨跌幅':
                    text = str(round(aline[8], 2)) + '%'  # 保留两位小数并加上单位
                else:
                    text = 'ERROR'

                label_attrs = attrs.copy()
                label_attrs['text'] = text
                label_attrs['fg'] = fg
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_attrs
                ).pack(expand='yes', fill='both')

            elif key == '名称/代码':
                # 一行存在两个数据
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[1]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[2]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '加入价格/时间':
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[3]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[4]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '最新价格/时间':
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[5]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[6]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '操作':
                # 操作按钮居中框架
                # --master cntl_dict[key]['CNTL'] 单元格框架
                frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
                frame_optionbtncneter = tk.Frame(
                    cntl_dict[key]['CNTL'],
                    frame_optionbtncneter_attrs
                )
                frame_optionbtncneter.pack(side='top', expand='yes')  # 水平垂直居中
                # 详情按钮
                # --master frame_optionbtncneter 单元格居中框架
                button_details_attrs = BUTTON_ATTRS.copy()
                button_details_attrs['text'] = '卖出'
                button_details = tk.Button(
                    frame_optionbtncneter,
                    button_details_attrs
                )
                button_details.pack(side='left', padx=5)
                # 绑定详情事件
                button_details.configure(
                    command=lambda
                    oder=aline[0],  # 库中id
                    : self.event_business(oder)
                )

    def monitor_hold_tdresult(self):
        """
        监听更新持有结果

        :return:
        """
        # 只有当前处于持有页面才继续监听
        if self.window.NowFaceCode == 1:
            print('当前处于持有页面')
            # 计算时间
            if calc_time():
                # 判断当前是否交易日
                open = isopen()
                if open > 0:
                    # 监听延时到开盘
                    print(f'延时 {open}s 监听！')
                    self.window.ROOT.after(open*1000, self.monitor_hold_tdresult)
                elif open < 0:
                    print('交易已结束或不是交易日！')
                    # 停用监听
                    self.window.monitorState_Hold = 'Not open'
                else:
                    print('正在交易中...')
                    # 判断是否有线程在执行
                    if self.window.tdObject_Hold:
                        print('当前有线程执行')
                        # 当前有线程在执行，获取线程结果
                        tdResult = self.window.tdObject_Hold.get_result()
                        if tdResult == 'success':
                            print('后台更新完成！')
                            # 更新完成，刷新页面
                            self.refresh()
                            # 开启新的更新最新价线程
                            self.window.td_run_hold()
                            self.window.ROOT.after(2000, self.monitor_hold_tdresult)

                        elif tdResult == 'Network error':
                            messagebox.showerror(title='网络错误', message='网络连接错误，实时刷新持有股票最新价格将失效！！！')
                            self.window.monitorState_Hold = False
                            self.window.tdObject_Hold = None  # 清空线程
                        elif tdResult == 'None data':
                            print('无持有，延时监听15s')
                            # 如果没有持有数据，继续监听
                            self.window.ROOT.after(15000, self.monitor_hold_tdresult)
                        elif tdResult == 'Time out':
                            # 询问是否重试
                            asktry = messagebox.askretrycancel(
                                title='实时刷新超时',
                                message='实时更新持有最新市价数据超时，是否重试？如果取消，将无法实时更新持有最新数据！'
                            )
                            if asktry:
                                # 开启新的更新最新价线程
                                self.window.td_run_hold()
                                self.window.ROOT.after(2000, self.monitor_hold_tdresult)
                            else:
                                self.window.monitorState_Hold = False
                                self.window.tdObject_Hold = None  # 清空线程
                        else:
                            print('线程未结束！')
                            self.window.ROOT.after(1000, self.monitor_hold_tdresult)
                    else:
                        print('当前无线程')
                        # 开启新的更新最新价线程
                        self.window.td_run_hold()
                        self.window.ROOT.after(2000, self.monitor_hold_tdresult)
            else:
                messagebox.showerror(title='时间错误', message='计算时间错误！！！')
                # 停用监听
                self.window.monitorState_Hold = 'CalcTime error'
        else:
            print('当前不在持有页面！')
            self.window.monitorState_Hold = False
            self.window.tdObject_Hold = None  # 清空线程


class HoldHistoryFace(Tabel):
    def __init__(self, window):
        Tabel.__init__(self, window)
        # --No.1--初始化排序----------
        self.Mode = 2  # 持有历史
        self.SortColumn = 'UpAndDownRange'
        self.SortMode = 'desc'

        # --No.2--初始化统计----------
        self.TotalText = ('历史总成本', '卖出总市值', '历史总收益', '历史总收益率')

        # --No.3--初始化表头----------
        self.TheadText = {
            '名称/代码': {'param': ('Name', 'Code'), 'width': 0.22},
            '加入价格/时间': {'param': ('BuyPrice', 'BuyDate'), 'width': 0.23},
            '卖出价格/时间': {'param': ('SalePrice', 'SaleDate'), 'width': 0.23},
            '涨跌': {'param': ('UpAndDownPrice',), 'width': 0.16},
            '涨跌幅': {'param': ('UpAndDownRange',), 'width': 0.16},
        }  # param不会空表示，该表头可排序。内容即，sort需要的参数。使用关键字参数

        # 绘制页面
        self.refresh()

    def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
        """
        重写父类，方法。绘制一行数据

        :param rowsindex:
        :param holdlist:
        :param btn3state:
        :param place:
        :return:
        """
        aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
        # 通用属性
        attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_THEME,
            'font': FONT_TBDATA1,
        }
        # 绘制行框架
        cntl_dict = self.draw_frame_aline(place=place)
        # 遍历绘制单元格数据
        for key, cntl in cntl_dict.items():
            if key in ('涨跌', '涨跌幅'):
                # 根据涨跌设置字体颜色
                fg = COLOR_THEME
                if aline[7] > 0:
                    fg = COLOR_RISE
                elif aline[7] < 0:
                    fg = COLOR_FALL
                # 判断是哪一列
                if key == '涨跌':
                    text = round(aline[7], 2)
                elif key == '涨跌幅':
                    text = str(round(aline[8], 2)) + '%'
                else:
                    text = 'ERROR'

                label_attrs = attrs.copy()
                label_attrs['text'] = text
                label_attrs['fg'] = fg
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_attrs
                ).pack(expand='yes', fill='both')

            elif key == '名称/代码':
                # 一行存在两个数据
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[1]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[2]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '加入价格/时间':
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[3]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[4]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '卖出价格/时间':
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[5]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[6]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '操作':
                # 操作按钮居中框架
                # --master cntl_dict[key]['CNTL'] 单元格框架
                frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
                frame_optionbtncneter = tk.Frame(
                    cntl_dict[key]['CNTL'],
                    frame_optionbtncneter_attrs
                )
                frame_optionbtncneter.pack(side='top', expand='yes')  # 水平垂直居中
                # 详情按钮
                # --master frame_optionbtncneter 单元格居中框架
                button_details_attrs = BUTTON_ATTRS.copy()
                button_details_attrs['text'] = '卖出'
                button_details = tk.Button(
                    frame_optionbtncneter,
                    button_details_attrs
                )
                button_details.pack(side='left', padx=5)
                # 绑定详情事件
                button_details.configure(
                    command=lambda
                    oder=aline[0],  # 库中id
                    : self.event_business(oder)
                )


class ChoiceResult(Tabel):
    def __init__(self, window, sortcol='bcynum', sortmode='desc', nowpagenum=0):
        Tabel.__init__(self, window)
        # --初始化排序
        self.SortColumn = sortcol
        self.SortMode = sortmode

        # --初始化页数
        self.NowPageNum = nowpagenum

        # --初始化本页面需要的图片
        self.window.tkImage['ChoiceResult'] = []
        # --页面都用到的图片
        self.window.NeedImgPath = [
            'skin/Choices/AnalysisStatus/thumb.png',
        ]
        self.window.imginit('ChoiceResult')  # 初始化所需通用图片

        # --初始化表头----------
        self.TheadText = {
            '序号': {'param': None, 'width': 0.08},
            '名称/代码': {'param': None, 'width': 0.18},
            '被持数': {'param': ('bcynum',), 'width': 0.1},
            '被持股数(万股)': {'param': None, 'width': 0.13},
            '评级': {'param': ('pj',), 'width': 0.2},
            '操作': {'param': None, 'width': 0.31},
        }  # param不会空表示，该表头可排序。内容即，sort需要的参数。使用关键字参数

        # 绘制页面
        self.refresh()

    def read_tbdata(self):
        """
        重写数据处理函数

        :return:
        """
        # 如果分析结果为空
        if not self.window.tdResult_Choice['data']:
            self.Tbody_DataLIST = None
        else:
            if isinstance(self.window.tdResult_Choice['data'], str):
                self.Tbody_DataLIST = self.window.tdResult_Choice['data']
            else:
                # 排序
                if self.SortMode == 'desc':
                    reverse = True
                elif self.SortMode == 'asc':
                    reverse = False
                else:
                    print(f'排序方式 参数错误！')
                    return None

                if self.SortColumn == 'pj':
                    # 按照评级排序
                    data = sorted(self.window.tdResult_Choice['data'].items(), key=lambda kv: (kv[1]['recommendIndex'], kv[0]),
                                  reverse=reverse)
                elif self.SortColumn == 'bcynum':
                    # 按照被持有数排序
                    data = sorted(self.window.tdResult_Choice['data'].items(),
                                  key=lambda kv: (kv[1]['recomStockInfo'][2], kv[0]),
                                  reverse=reverse)
                else:
                    print(f'排序列 参数错误！')
                    return None

                print(f'排序结果：{data}')
                # 处理数据
                oder = 1
                temp_Tbody_DataLIST = []
                for v in data:
                    temp_Tbody_DataLIST.append(
                        (
                            oder,  # 序号
                            v[1]['recomStockInfo'][0],  # 股票名称
                            v[1]['recomStockInfo'][1],  # 股票代码
                            v[1]['recomStockInfo'][2],  # 被持数
                            v[1]['recomStockInfo'][3],  # 被持股数（万股）
                            v[1]['recommendIndex'],  # 评级
                            v[1]['beHoldJJ']  # 被持基金信息
                        )
                    )  # 一行数据
                    oder += 1  # 序号自增
                # 将数据处理成分页数据
                self.Tbody_DataLIST = handle_paging(temp_Tbody_DataLIST, self.PageNum)

                self.LENDataLIST = 0  # 重置总数
                # 统计数据
                if self.Tbody_DataLIST:
                    for page in self.Tbody_DataLIST:
                        self.LENDataLIST += len(page)  # 总数据条目
                    self.PageTotalNum = len(self.Tbody_DataLIST)  # 总页数

    def draw_tname(self):
        """

        :return:
        """

        # --绘制基本信息--------------------------------
        # 返回按钮框架属性
        frame_back_attrs = FRAME_ATTRS.copy()
        # frame_back_attrs['bg'] = 'yellow'
        frame_back_place = {
            'width': SIZE_TBNAME_W * 0.15,
            'height': SIZE_TBNAME_H,
        }
        # 表格标题框架属性
        frame_tbtitle_attrs = FRAME_ATTRS.copy()
        # frame_tbtitle_attrs['bg'] = 'pink'
        frame_tbtitle_place = {
            'width': SIZE_TBNAME_W - frame_back_place['width'],
            'height': SIZE_TBNAME_H,
        }

        # 表格标题框架
        # --master self.CNTL_Tabel['name'] 表格名称框架
        frame_tbtitle = tk.Frame(
            self.CNTL_Tabel['name'],
            frame_tbtitle_attrs
        )
        frame_tbtitle.place(frame_tbtitle_place)
        # --通用属性--
        label_currency_attrs = {
            'bg': COLOR_BACKGROUND,
        }
        # 风险提示
        # --master frame_tbtitle 表格标题框架
        label_warning_attrs = label_currency_attrs.copy()
        label_warning_attrs['anchor'] = 'w',
        label_warning_attrs['fg'] = COLOR_TIPS_LOW
        label_warning_attrs['font'] = FONT_WARNING
        label_warning_attrs['text'] = '以下数据仅供参考，不构成任何投资建议。股市有风险，入市须谨慎！'
        label_warning = tk.Label(
            frame_tbtitle,
            label_warning_attrs
        )
        label_warning.pack(side='bottom', fill='x')
        # 表格标题
        # --master frame_tbtitle 表格标题框架
        label_title_attrs = label_currency_attrs.copy()
        label_title_attrs['anchor'] = 'w'
        label_title_attrs['fg'] = COLOR_TIPS_TALL
        label_title_attrs['font'] = FONT_TBNAME
        label_title_attrs['text'] = self.window.tdResult_Choice['TabelName']
        # 绘制结果信息头
        label_title = tk.Label(
            frame_tbtitle,
            label_title_attrs
        )
        label_title.pack(side='bottom', fill='x')

        # 返回按钮框架
        # --master self.CNTL_Tabel['name'] 表格名称框架
        frame_back_place['x'] = frame_tbtitle_place['width']
        frame_back = tk.Frame(
            self.CNTL_Tabel['name'],
            frame_back_attrs
        )
        frame_back.place(frame_back_place)
        # 返回按钮
        # --master frame_back 返回按钮框架
        button_back_attrs = BUTTON_ATTRS.copy()
        button_back_attrs['bd'] = 0
        button_back_attrs['font'] += ' underline'
        button_back_attrs['text'] = '返回'
        button_back = tk.Button(
            frame_back,
            button_back_attrs
        )
        button_back.pack(side='top', expand='yes')
        button_back.configure(command=lambda: self.event_back())

    def draw_aline(self, rowsindex, holdlist, btn3state=False, place=None):
        """

        :param rowsindex:
        :param holdlist:
        :param btn3state:
        :param place:
        :return:
        """
        aline = self.Tbody_DataLIST[self.NowPageNum][rowsindex]
        # 通用属性
        attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_THEME,
            'font': FONT_TBDATA1,
        }
        # 绘制行框架
        cntl_dict = self.draw_frame_aline(place=place)
        # 遍历绘制单元格数据
        for key, cntl in cntl_dict.items():
            if key in ('序号', '被持数', '被持股数(万股)'):
                # 判断是哪一列
                if key == '序号':
                    text = aline[0]
                elif key == '被持数':
                    text = aline[3]
                elif key == '被持股数(万股)':
                    text = '%.2f' % aline[4]
                else:
                    text = 'ERROR'

                label_attrs = attrs.copy()
                label_attrs['text'] = text
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_attrs
                ).pack(expand='yes', fill='both')

            elif key == '名称/代码':
                # 一行存在两个数据
                # --top
                label_top_attrs = attrs.copy()
                label_top_attrs['text'] = aline[1]
                label_top_attrs['anchor'] = 's'
                label_top_place = {
                    'width': cntl_dict[key]['place']['width'],
                    'height': cntl_dict[key]['place']['height'] / 2,
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_top_attrs
                ).place(label_top_place)

                # --bottom
                label_bottom_attrs = attrs.copy()
                label_bottom_attrs['fg'] = COLOR_TIPS_TALL
                label_bottom_attrs['font'] = FONT_TBDATA2
                label_bottom_attrs['text'] = aline[2]
                label_bottom_attrs['anchor'] = 'n'
                label_bottom_place = {
                    'width': label_top_place['width'],
                    'height': label_top_place['height'],
                    'y': label_top_place['height']
                }
                # 绘制控件
                tk.Label(
                    cntl_dict[key]['CNTL'],
                    label_bottom_attrs
                ).place(label_bottom_place)

            elif key == '评级':
                # 评级原理：底图30*30图片*3.利用遮挡达到评级的效果
                # 最低显示一个，最高显示全部
                canvas_grade_attrs = CANVAS_ATTRS.copy()
                # canvas_grade_attrs['bg'] = 'red'
                canvas_grade_attrs['width'] = 30 * 3  # 3张30*30的评级图片
                canvas_grade_attrs['height'] = 30
                canvas_grade = tk.Canvas(
                    cntl_dict[key]['CNTL'],
                    canvas_grade_attrs
                )
                canvas_grade.pack(side='top', expand='yes')
                # 绘制图片
                canvas_grade.create_image(45, 15, image=self.window.tkImage['ChoiceResult'][0])
                # 根据推荐指数，确定遮挡部分大小
                # 如 推荐指数：0.5. width = 60 * 0.5
                # 判断评级是否超过 1
                if aline[5] > 1.0000000:
                    print(f'ERROR: recommendIndex > 1')
                    time.sleep(10)
                    exit()
                # --master canvas_grade 挡板
                frame_baffle_attrst = FRAME_ATTRS.copy()
                frame_baffle_place = {
                    'width': 60 - 60 * aline[5],
                    'height': 30,
                    'x': 30,
                }
                frame_baffle_place['x'] += 60 - frame_baffle_place['width']
                frame_baffle = tk.Frame(
                    canvas_grade,
                    frame_baffle_attrst
                )
                frame_baffle.place(frame_baffle_place)

            elif key == '操作':
                # 操作按钮居中框架
                # --master cntl_dict[key]['CNTL'] 单元格框架
                frame_optionbtncneter_attrs = FRAME_ATTRS.copy()
                frame_optionbtncneter = tk.Frame(
                    cntl_dict[key]['CNTL'],
                    frame_optionbtncneter_attrs
                )
                frame_optionbtncneter.pack(side='top', expand='yes')  # 水平垂直居中
                # 详情按钮
                # --master frame_optionbtncneter 单元格居中框架
                button_details_attrs = BUTTON_ATTRS.copy()
                button_details_attrs['text'] = '详情'
                button_details = tk.Button(
                    frame_optionbtncneter,
                    button_details_attrs
                )
                button_details.pack(side='left', padx=5)
                # 绑定详情事件
                button_details.configure(
                    command=lambda
                    pre1=self.window,
                    pre2=aline[2],  # 股票代码
                    pre3=self.SortColumn,
                    pre4=self.SortMode,
                    pre5=self.NowPageNum,
                    : StockInfo(
                        pre1, pre2, pre3, pre4, pre5
                    )
                )
                # 加持有按钮
                # --master frame_optionbtncneter 单元格居中框架
                button_details_attrs = BUTTON_ATTRS.copy()
                button_details = tk.Button(
                    frame_optionbtncneter,
                    button_details_attrs
                )
                button_details.pack(side='left', padx=5)
                # 判断是否已持有
                if not aline[2] in holdlist:
                    button_details['text'] = '加持有'
                    # 绑定加持有事件
                    button_details.configure(command=lambda par=aline[2]: self.event_business(par))
                else:
                    button_details['text'] = '已持有'
                    button_details['state'] = 'disable'  # 禁用按钮
                    # 不绑定事件
                # 展开按钮
                # --master frame_optionbtncneter 单元格居中框架
                button_details_attrs = BUTTON_ATTRS.copy()
                button_details = tk.Button(
                    frame_optionbtncneter,
                    button_details_attrs
                )
                button_details.pack(side='left', padx=5)
                # 绑定展开按钮事件
                if not btn3state:
                    button_details['text'] = '展开'
                    button_details.configure(command=lambda pre1=rowsindex: self.draw_heldjjinfo(pre1))
                else:
                    button_details['text'] = '收起'
                    # 返回
                    button_details.configure(command=lambda: self.refresh())

    def draw_heldjjinfo(self, index: int):
        """
        绘制股票被持有基金信息

        :param index: 被持有数据的下标
        :return:
        """
        # 禁用表头所有按钮
        self.disabel_theadbtn()

        aline = self.Tbody_DataLIST[self.NowPageNum][index]
        print(f'绘制被持有 {aline}')
        # 清空表身框架
        self.destroy_tbody()
        # 清空分页框架
        self.destroy_tpage()

        # 读取已持有列表
        holdlist = Ldb.read_holdlist()
        # 股票信息
        aline_place = {
            'width': SIZE_TLINE_W,
            'height': SIZE_TLINE_H
        }
        self.draw_aline(index, holdlist, btn3state=True, place=aline_place)

        # 被持有框架
        # --master self.CNTL_Tabel['tbody']  # 表身框架
        frame_heldjj_attrs = FRAME_ATTRS.copy()
        # frame_heldjj_attrs['bg'] = 'red'
        frame_heldjj_palce = {
            'width': SIZE_TBODY_W - SIZE_TBHELDJJ_MARGIN * 2,
            'height': SIZE_TBODY_H - SIZE_TLINE_H,
            'y': SIZE_TLINE_H,
            'x': SIZE_TBHELDJJ_MARGIN
        }
        frame_heldjj = tk.Frame(
            self.CNTL_Tabel['tbody'],
            frame_heldjj_attrs
        )
        frame_heldjj.place(frame_heldjj_palce)
        # 被持基金基本属性
        alienheldjj_attrs = CANVAS_ATTRS.copy()
        # alienheldjj_attrs['bg'] = 'pink'
        alienheldjj_attrs['width'] = frame_heldjj_palce['width']
        alienheldjj_attrs['height'] = SIZE_HELDJJLINE_H  # 被持有基金信息框架的高度
        # 被持有基金信息列表
        datalist_totalnum = len(aline[6])
        # 判断是否需要滚动条
        if datalist_totalnum * SIZE_HELDJJLINE_H > frame_heldjj_palce['height']:
            # 需要滚动条
            mysf = MyScrollFrame(frame_heldjj)
            mysf.pack(side="top", fill="both", expand=True)  # 打包滚动条
            cntl_frame_heldjj = mysf.get_container()  # 返回可滚动视图，即表身数据容器
        else:
            # 不需要滚动条
            cntl_frame_heldjj = frame_heldjj

        # 单元格基本属性
        label_cell_attrs = {
            'bg': COLOR_BACKGROUND,
            'fg': COLOR_TIPS_TALL,
            'font': FONT_HELDJJINFO
        }
        label_cell_place = {
            'height': alienheldjj_attrs['height'] - SIZE_TBLINE_WIDTH
        }
        # 遍历绘制被持基金信息
        for jjinfos in aline[6]:
            # 被持有基金一行框架
            # --master frame_heldjj 被持有框架
            frame_alineheldjj = tk.Canvas(
                cntl_frame_heldjj,
                alienheldjj_attrs
            )
            frame_alineheldjj.pack()
            # 绘制表格线条
            draw_tb_line(frame_alineheldjj, alienheldjj_attrs, tt=(0, 1))
            # 单元格宽度平分一行框架
            width = alienheldjj_attrs['width'] / len(jjinfos)
            mul = 0
            for jjinfo in jjinfos:
                # 单元格
                # --master frame_alineheldjj 一行框架
                if mul == 1:
                    # 给持有股份加上单位
                    label_cell_attrs['text'] = jjinfo + "(万股)"
                else:
                    label_cell_attrs['text'] = jjinfo
                label_cell_place['width'] = width
                label_cell_place['x'] = mul * width
                label_cell = tk.Label(
                    frame_alineheldjj,
                    label_cell_attrs
                )
                label_cell.place(label_cell_place)
                mul += 1

    def event_back(self):
        """
        返回按钮事件，返回到选股页面

        :return:
        """
        asktry = messagebox.askokcancel(title='确认', message='如果返回，本次结果将被清空！是否确定返回？')
        if asktry:
            # 清空线程结果
            self.window.tdObject_Choice = None
            self.window.tdResult_Choice = None
            # 重新绘制选股页面
            ChoiceFace(self.window)


if __name__ == "__main__":
    run = WINDOW()
    run.ROOT.mainloop()
